在我开始写程序时因为担心某些分支下忘记释放内存导致泄漏,就想能不能保险点,多加几次释放,但很快发现堆内存不能重复释放,一些错误释放甚至会导致系统崩溃。这类错误可分几种情况:
1)重复释放某指针指向的内存,多数由于调用了不同层的子函数重复释放同一内存,如:
int* p = malloc(20);
……
free (p);
……
System_Free() //此函数内再次free(p),程序员没有注意
有人说,释放前加上空指针判断就能避免这个问题。即:if(p!=null) free(p);
可单单这样做并没有效果!人们往往认为free(p)的对象是指针p,而实际free(p)只是把指针p指向的内存释放,并不改变指针p本身。也就是说,free不会强制清零指针p,free之后p仍指向原来内存块,只是这块内存已不可用而已。既然p没有被free清零,判断空指针的操作就不起作用。所以完整做法还要在free后加上指针清零,这样空指针判断才有用,即:
if(p!=null)
{
free(p);
p = null;
}
2)即使free后指针清0,free前做非0检查,但如果不同指针指向同一内存,又分别被free呢,如:
int* p1 = malloc(20);
int* p2 = p1; //p2,p1指向同一地址
……;
free( p1);
free( p2); //错误,p2所指内存已被free( p1)释放,不能重复释放同一块内存。
这种情况也是重复free同一内存,但用1)的方法没法预防。
3) free操作的指针不是指向堆内存:
int a = 100;
int* p = &a;
free( p); //ERROR, p don’t point to heap, and stack can’t be freed by free()
除了malloc返回的堆内存,其他如栈内存/数据区等不能用free释放。
4) free操作的指针不是malloc返回的内存块起始指针:
int* p = malloc(20);
p++;
……
free(p); //ERROR, p doesn’t point to the start address of a memory block
free操作的指针必须是已分配的某块堆内存的起始指针,移动后的指针不指向内存块起始,被free也会crash。
5) free逻辑错误导致野指针,多由于free时机不对,比如两指针指向同一块内存时:
int* a = (int*)malloc(sizeof(int));
int* b = a;
free(a);
*b = 0; //访问野指针
这种类型错误还包括:子函数中包含释放某指针形参所指内存的操作,而主函数不知情,在调用此子函数之后还继续使用该内存;链表释放时先释放某节点内存,又试图通过此节点访问并摘除后续节点。这些都是错误释放导致的野指针访问错误。关于野指针,后叙。
如果程序总在退出时crash,就要检查是否有错误free。