在我開始寫程序時因爲擔心某些分支下忘記釋放內存導致泄漏,就想能不能保險點,多加幾次釋放,但很快發現堆內存不能重複釋放,一些錯誤釋放甚至會導致系統崩潰。這類錯誤可分幾種情況:
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。