一道思考题:
C语言中对于表达式中存在有符号数和无符号类型时,将对计算过程进行如何处理,经常听到的说法是:
“当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型”
以上这种表述是不准确的,正确的结论应为:
“当表达式中存在有符号类型和无符号类型时,默认情况下计算的结果将转化为无符号类型”
而对于计算过程而言,变量本身转化为有符号还是无符号数,都不会改变在计算机中存储的位状态。
参考如下代码:
不可重入函数不可以在它还没有返回就再次被调用。例如printf,malloc,free等都是不可重入函数。因为中断可能在任何时候发生,例如在printf执行过程中,因此不能在中断处理函数里调用printf,否则printf将会被重入。
函数不可重入大多数是因为在函数中引用了全局变量。例如,printf会引用全局变量stdout,malloc,free会引用全局的内存分配表。
个人理解:如果中断发生的时候,当运行到printf的时候,假设发生了中断嵌套,而此时stdout资源被占用,所以第二个中断printf等待第一个中断的stdout资源释放,第一个中断等待第二个中断返回,造成了死锁,不知这样理解对不对。
不可重入函数指的是该函数在被调用还没有结束以前,再次被调用可能会产生错误。可重入函数不存在这样的问题。
不可重入函数在实现时候通常使用了全局的资源,在多线程的环境下,如果没有很好的处理数据保护和互斥访问,就会发生错误。
常见的不可重入函数有:
printf --------引用全局变量stdout
malloc --------全局内存分配表
free --------全局内存分配表
在unix里面通常都有加上_r后缀的同名可重入函数版本。如果实在没有,不妨在可预见的发生错误的地方尝试加上保护锁同步机制等等。
这种情况出现在多任务系统当中,在任务执行期间捕捉到信号并对其进行处理时,进程正在执行的指令序列就被信号处理程序临时中断。如果从信号处理程序返回,则继续执行进程断点处的正常指令序列,从重新恢复到断点重新执行的过程中,函数所依赖的环境没有发生改变,就说这个函数是可重入的,反之就是不可重入的。
众所周知,在进程中断期间,系统会保存和恢复进程的上下文,然而恢复的上下文仅限于返回地址,cpu寄存器等之类的少量上下文,而函数内部使用的诸如全局或静态变量,buffer等并不在保护之列,所以如果这些值在函数被中断期间发生了改变,那么当函数回到断点继续执行时,其结果就不可预料了。打个比方,比如malloc,将如一个进程此时正在执行malloc分配堆空间,此时程序捕捉到信号发生中断,执行信号处理程序中恰好也有一个malloc,这样就会对进程的环境造成破坏,因为malloc通常为它所分配的存储区维护一个链接表,插入执行信号处理函数时,进程可能正在对这张表进行操作,而信号处理函数的调用刚好覆盖了进程的操作,造成错误。
满足下面条件之一的多数是不可重入函数:
(1)使用了静态数据结构;
(2)调用了malloc或free;
(3)调用了标准I/O函数;标准io库很多实现都以不可重入的方式使用全局数据结构。
(4)进行了浮点运算.许多的处理器/编译器中,浮点一般都是不可重入的 (浮点运算大多使用协处理器或者软件模拟来实现。
1) 信号处理程序A内外都调用了同一个不可重入函数B;B在执行期间被信号打断,进入A (A中调用了B),完事之后返回B被中断点继续执行,这时B函数的环境可能改变,其结果就不可预料了。
2) 多线程共享进程内部的资源,如果两个线程A,B调用同一个不可重入函数F,A线程进入F后,线程调度,切换到B,B也执行了F,那么当再次切换到线程A时,其调用F的结果也是不可预料的。
在信号处理程序中即使调用可重入函数也有问题要注意。作为一个通用的规则,当在信号处理程序中调用可重入函数时,应当在其前保存errno,并在其后恢复errno。(因为每个线程只有一个errno变量,信号处理函数可能会修改其值,要了解经常被捕捉到的信号是SIGCHLD,其信号处理程序通常要调用一种wait函数,而各种wait函数都能改变errno。)
写一个函数,完成内存之间的拷贝。[考虑问题是否全面,是否考虑内存重叠问题]
返回void *支持链式操作,参数类型是void *以支持任意类型的指针,输入参数加上const修饰,最好加上assert对输入输出指针进行非NULL判断
void* memcpy( void *dest, const void *src, size_t count )
{
char* pdest = static_cast<char*>( dest );
const char* psrc = static_cast<const char*>( src );
// 依次从前拷贝,目的地址覆盖了源地址的数,此时从后往前拷贝
if( (pdest>psrc) && (pdest<(psrc+count))) //能考虑到这种情况就行了
{
for( size_t i=count-1; i!=-1; --i )
pdest[i] = psrc[i];
}
else
{
for( size_t i=0; i<count; ++i )
pdest[i] = psrc[i];
}
return dest;
if ((ptr = (char *)malloc(0)) == NULL)
puts( "Got a null pointer ");
else
puts( "Got a valid pointer ");
上面程序在VC6.0下输出结果是:Got a valid pointer
请问指针为NULL时指向哪里,分配的空间为0时又指向哪里?
当使用malloc后,只有在没有足够内存的情况下会返回NULL,或是出现异常报告。
malloc(0),系统就已经帮你准备好了堆中的使用起始地址(不会为NULL)。但是你不能对该地址进行写操作(不是不允许),如果写了话,当调用free(ptr)就会产生异常报告(地址受损)。
NULL 一般预定义为 (void *)0,指向0地址。malloc是在程序堆栈上分配空间,不会是0地址
malloc(0)是指分配内存大小为零
NULL是不指向任何实体
malloc(0)也是一种存在不是NULL
联系客服