【问题】
下列代码的输出结果是什么:
char *ptr = NULL;
if ((ptr = (char *)malloc(0)) == NULL)
puts("Got a null pointer\n");
else
puts("Got a valid pointer\n");
【答案】
got a Valid pointer
【解析】
①C99的最权威的解释:
If the size of the space requested is zero, the behavior is implementationdefined:
either a null pointer is returned, or the behavior is as if the size were some
nonzero value, except that the returned pointer shall not be used to access an object.
②man malloc:
If size is 0, then malloc() returns either NULL, or a unique pointer value that can later be successfully passed to free().
③MSDN
If size is 0, malloc allocates a zero-length item in the heap and returns a valid pointer to that item
【总结】
当调用malloc(0)时,可能返回NULL指针,也可能返回一个有效的指针,这是由编译器的实现决定的。
返回有效指针时,该指针可以被正常free,但是不应该其进程操作。
当malloc分配内存时它除了分配我们指定SIZE的内存块,还会分配额外的内存来存储我们的内存块信息,用于维护该内存块。
因此,malloc(0)返回一个合法的指针并指向存储内存块信息的额外内存,
我们当然可以在该内存上进行读写操作,但是这样做了会破坏该内存块的维护信息,
因此当我们调用free(ptr)时就会出现错误。
首先来解释malloc(0)的问题,这个语法是对的,而且确实也分配了内存,但是内存空间是0,
就是说返回给你的指针是不能用的,感觉奇怪吧?但是从操作系统的原理来解释就不奇怪了,
这要涉及操作系统维护内存的方法来说了,在内存管理中,内存被分为2部分,栈和堆,栈有自己的机器指令,由系统分配,
是一个先进后出的数据结构,malloc分配的内存是堆内存,由于堆没有自己的机器指令,
所以要有系统自己编写算法(库函数)来管理这片内存,通常的做法是用链表,在每片被分配的内存前加个表头,
里面存储了被分配内存的起始地址和大小,你的malloc返回的就是表头里的起始指针,这个地址是由一系列的算法得来了,
通常不会为0,一旦分配成功,就返回一个有效的指针,对于分配0空间来说,算法已经算出可用内存的起始地址,
但是你占用0空间,所以对那个指针操作就是错误的,操作系统一般不知道其终止地址,因为有占用大小就可以推出终止地址,
还有就是即使分配0空间也要释放它,其实是释放的链表结点 。
还有,返回的指针是可用地址的起始地址,虽然你可以无限赋值,但是其实是错误的,
因为可能有其他有用的数据在那一片区域,如果指针越界就会出现意想不到的事情。