經典C記憶體管理疑難雜例(轉載)
一定要弄懂GetMemory
堆疊
棧中分配區域性變數空間,是系統自動分配空間。定義一個 char a;系統會自動在棧上為其開闢空間。由於棧上的空間是自動分配自動回收的,所以棧上的資料的生存週期只是在函式的執行過程中,執行後就釋放掉,不可以再訪問。
堆區分配程式設計師申請的記憶體空間,堆上的資料只要程式設計師不釋放空間,就一直可以訪問到,不過缺點是一旦忘記釋放會造成記憶體洩露。
靜態區是分配靜態變數,全域性變數空間的。
inta=0;全域性初始化區 char*p1;全域性未初始化區 main(){ intb;//棧 chars[]="abc";//棧 char*p2;//棧 char*p3="123456";//123456\0在常量區,p3在棧上。 staticintc=0;//全域性(靜態)初始化區 p1=(char*)malloc(10);//堆 }
GetMemory1
voidGetMemory1(char*p)
{
p=(char*)malloc(100);
}
voidTest1(void)
{
char*str=NULL;
GetMemory1(str);
strcpy(str,"helloworld");
printf(str);//str一直是空,程式崩潰
}
結果:
分析:
毛病出在函式GetMemory1 中。編譯器總是要為函式的每個引數製作臨時副本,指標引數p的副本是 _p,編譯器使 _p = p。如果函式體內的程式修改了_p的內容,就導致引數p的內容作相應的修改。這就是指標可以用作輸出引數的原因。在本例中,_p申請了新的記憶體,只是把 _p所指的記憶體地址改變了,但是p絲毫未變。所以函式GetMemory並不能輸出任何東西。事實上,每執行一次GetMemory1就會洩露一塊記憶體,因為沒有用free釋放記憶體。Test1中呼叫GetMemory1時,函式引數為str的副本不是str本身
GetMemory2
voidGetMemory2(char**p,intnum)
{
*p=(char*)malloc(num);
}
voidTest2(void)
{
char*str=NULL;
GetMemory2(&str,100);
strcpy(str,"hello");
printf(str);
}
結果:輸出hello
分析:動態分配的記憶體不會自動釋放;
沒有測試是否成功分配了記憶體,應該有if (*p == NULL) { ……} 之類的語句處理記憶體分配失敗的其情況。
GetMemory3
char*GetMemory3(void) { charp[]="helloworld"; returnp; } voidTest3(void) { char*str=NULL; str=GetMemory3(); printf(str); }
結果:輸出亂碼。
分析:字元陣列p存在於棧空間,是區域性變數,函式返回後,記憶體空間被釋放,因此輸出無效值。字元陣列的值是可以修改的,例如p[0] = 't‘。
GetMemory4
char*GetMemory4(void)
{
char*p="hello";
returnp;
}
voidTest4(void)
{
char*str=NULL;
str=GetMemory4();
cout<<str<<endl;
}
結果:輸出hello
分析:p指向的是字串常量,字串常量儲存在只讀的資料段,是全域性區域,但不是像全域性變數那樣儲存在普通資料段(靜態儲存區)。無法對p所指的記憶體的內容修改,例如p[0] = 'y;這樣的修改是錯誤的。
GetMemory5
char*GetMemory5(void)
{
return"hello";
}
voidTest3(void)
{
char*str=NULL;
str=GetMemory5();
printf(str);
}
結果:輸出hello
分析:直接返回常量區。
GetMemory6
voidGetMemory6(void){
char*str=(char*)malloc(100);
strcpy(str,"hello");
free(str);
//str=NULL,加上這句程式才不會有野指標
if(str!=NULL){
strcpy(str,"world");
printf(str);
}
}
voidmain(){
GetMemory6();
}
結果:能夠輸出world,但程式存在問題。
分析:程式出現了野指標。
野指標只會出現在像C和C++這種沒有自動記憶體垃圾回收功能的高階語言中,所以java或c#肯定不會有野指標的概念.當我們用malloc為一個指標分配一個空間後,用完這個指標,把它free掉,但是沒有讓這個指標指向NULL或某一個特定的空間。如上面程式一樣,將str進行free後,只是釋放了指標所指的記憶體,但指標並沒有釋放掉,此時指標所指的是垃圾記憶體;這樣的話,if語句永為真,if判斷無效。delete也存在同樣的問題。
防止產生野指標:
- 指標變數一定要初始化為NULL,因為任何指標變數剛被建立時不會自動成為NULL指標,它的預設值是隨機的。
- 當free或delete後,將指標指向NULL。通常判斷一個指標是否合法,都是使用if語句測試該指標是否為NULL。