最近看到一个很奇怪的问题,就是malloc(0),返回的却不是NULL,但是free这块内存时却报错。
程序跑起来的内存可以分为栈和堆,栈有自己的机器指令,是一个先进后出的数据结构,我就在这里不再过多解释了,malloc分配的内存是堆内存,由于堆没有自己的机器指令,所以要有系统自己编写算法来管理这片内存,通常的做法是用链表,在每片被分配的内存前加个表头,里面存储了被分配内存的起始地址和大小,你的malloc返回的就是表头里的起始指针,这个地址是由一系列的算法得来了,通常不会为0,一旦分配成功,就返回一个有效的指针,对于分配0空间来说,算法已经算出可用内存的起始地址,但是你占用0空间,所以对那个指针操作就是错误的,操作系统一般不知道其终止地址,因为有占用大小就可以推出终止地址,还有就是即使分配0空间也要释放它,其实是释放的链表结点。还有,返回的指针是可用地址的起始地址,可用大小是固定的,在VC6下是56字节,这个大小可能就是链表节点的大小,做了一个验证,代码如下:
int main()
{
char *ptr =NULL;
char *ptr2 = NULL;
char *ptr3 = NULL;
ptr = malloc(0*sizeof(char)) ;
ptr2 = malloc(32*sizeof(char));
ptr3 = malloc(32*sizeof(char));
//strcpy(ptr,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
strcpy(ptr2, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
strcpy(ptr3, "ccccccccccccccccccccccccccccccc");
printf("ptr : %x\n", ptr);
printf("ptr2 : %x\n", ptr2);
printf("ptr3 : %x\n", ptr3);
if (NULL == ptr)
printf("got a NULL pointer\n");
else
{
printf("got a Valid pointer\n");
// 有56个a,另外有一个字节用于保存''\0'
//strcpy(ptr,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
strcpy(ptr,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
//printf("the value at %X is:%c\n",ptr,*ptr);
//printf("the string at %x is :%s\n",ptr, ptr);
printf("the string ptr: %s\n", ptr);
printf("the string ptr2: %s\n", ptr2);
//printf("the string ptr2 + 56: %s\n", ptr2 + 56);
printf("the string ptr3: %s\n", ptr3);
//free(ptr);
}
return 0 ;
}
结果如下:
malloc(0)之后在malloc(32),两者返回的地址刚好相差56byt,应该就是VC6.0的维护内存的节点的大小了,然后ptr3的地址跟ptr2相差88(56 + 32)。
综合上面的验证,可以得出一个这样的结论,如果malloc成功,返回的其实是管理筷链表的起始地址,这个地址是不会为0的,但是可以使用的地址确是0,以为ptr2的地址为ptr+56,malloc真实使用的地址是malloc返回的地址加上头大小56开始,到sizeof地址处结束。超出这个范围的访问就是越界了