_BLOCK_TYPE_IS_VALID(pHead->nBlockUse 报错的两种情况
这个问题是新手容易犯的问题,一定要注意,一个malloc ,用完之后紧接着用一个free,然后赋值 NULL ,一步也不能少。
比如:
int *p=(int *)malloc(4);
int a[2]={1,3};
p=a;
free(p);
p=NULL;
这个代码,实际上,malloc 后 p 已经有了一个地址了(建议自己打断点看看),而且分配好内存了;此时用a的地址赋值给p,那么p原来的地址就找不到了,这个时候free(p)就找不到要释放的内存,报错。
int *p=(int *)malloc(4);
free(p);
p=NULL;
int a[2]={1,3};
p=a;
cout<<p[0]<<p[1]<<endl;
free(p);//这里会报错
p=NULL;
这个代码实际上是没有理解一个malloc对应一个free的精髓的所在。
可是既然已经前面已经free(p)了,为什么后面还是可以赋值呢?
因为p本质是还是指针,它可以被赋值。这就造成了似乎系统里还有p的动态分配内存的幻觉,实际上那个内存已经被释放了,再释放第二次自然会报错。
如果还想再用这个p,还要再次为其分配内存,再次分配内存的时候,编译器会在上次的指针指向的位置再次分配内存:
int* p = (int*)malloc(4);
p[0] = 1;
cout << p << endl;
free(p);
cout << p << endl;
p = NULL;
p = (int*)malloc(4);
free(p);
cout << p << endl;
输出的三个值相等。
但是实际上,当指针赋值给NULL 之后、就找不到之前对应的位置了。所以
如果在上文 p = NULL;之后加上cout<<p[0]<<endl;
会报错:
引发了异常: 读取访问权限冲突。
p 是 nullptr
所以说为什么会出现上面三个数字都相等的情况、我不太清楚。可以理解为编译器所为。
细心的朋友们可能会问为什么free() 之后都要 给指针赋值 NULL呢?
每次free()完之后都要赋值给NULL 的重要性1,避免出现悬空指针。
什么是悬空指针呢?看下例子:
int* p = (int*)malloc(4);
p[0] = 1;
cout << p[0] << endl;
free(p);
cout << p[0] << endl;
在free()之后,第二个cout仍然可以输出一个值:free()之后、指针仍然是指向该位置,输出该位置的值。这就是悬空指针。
在每次free()完之后赋值 NULL 还有一个重要性2:
int* p = (int*)malloc(8);
free(p);
//p = NULL;
free(p);
free(p);
这个代码会报错,因为重复释放了内存,但是如果去掉注释、就不会报错了。
原来当 free()的时候,会自动检测该指针是否是NULL,如果是的话,就不进行释放操作了、这样就避免了重复释放内存的错误,文章最开始出现的错误都不会出现了,所以大家要养成free()之后立刻赋值 NULL 的好习惯。
说到这里,提到一个很有趣的事情,看看下面两段代码:
int* p = (int*)malloc(2*sizeof(int));
//cout << p [0]<< endl;
p[0] = 1;
p[1] = 2;
//p[2] = 3;
//p[3] = 4;
free(p);
p=NULL;
int a[2];
a[0] = 1;
a[1] = 2;
//a[2] = 3;
第一个代码、取消注释之后,直到赋值到p[3]的时候、依然不会报错,只有当free(p)的时候才会报错:after Normal block(#165) at … 这句话的代码意味着你修改了分配的内存之外的内容。
第二个代码,取消注释之后,一旦赋值了a[2]就会直接报错;直接告诉你:Stack around the variable ‘a’ was corrupted…
作为指针、不能修改其分配的内存外面的内容,但是可以访问外面的内容。
int a[2];
a[0] = 1;
a[1] = 2;
cout<<a[100]<<endl;
比如这种,就不会报错。