1. 程式人生 > 其它 >經典C記憶體管理疑難雜例(轉載)

經典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。