標籤:
記憶體配置方式有三種:
(1) 從靜態儲存地區分配。記憶體在程式編譯的時候就已經分配好,這塊記憶體在程式的整個運行期間都存在。例如全域變數,static 變數。
(2) 在棧上建立。在執行函數時,函數內局部變數的儲存單元都可以在棧上建立,函數執行結束時這些儲存單元自動被釋放。棧記憶體分
配運算內建於處理器的指令集中,效率很高,但是分配的記憶體容量有限。
(3) 從堆上分配,亦稱動態記憶體分配。程式在啟動並執行時候用 malloc 或 new 申請任意多少的記憶體,程式員自己負責在何時用 free 或
delete 釋放記憶體。動態記憶體的生存期由我們決定,使用非常靈活,但問題也最多。動態記憶體的申請與釋放必須配對,防止記憶體流失。
在使用記憶體之前檢查指標是否為 NULL。如果指標 p 是函數的參數,那麼在函數的入口處用 assert(p!=NULL)進行檢查。
如果是用 malloc 或 new 來申請記憶體,應該用 if(p==NULL) 或 if(p!=NULL)進行防錯處理。
記憶體的預設初值究竟是什麼並沒有統一的標準,儘管有些時候為零值,我們寧可信其無不可信其有。所以無論用何種方式建立數組,
都別忘了賦初值,即便是賦零值也不可省略。
注意不要返回指向“棧記憶體”的“指標”或者“引用”,因為該記憶體在函數體結束自時被動銷毀。
使用 free 或 delete 釋放了記憶體後,將指標設定為 NULL。以防產生“野指標”。
數組名對應著(而不是指向)一塊記憶體,數組名相當於一個常量指標,其地址與容量在生命期內保持不變,只有數組的內容可以改變。
指標可以隨時指向任意類型的記憶體塊,它的特徵是“可變”,所以我們常用指標來操作動態記憶體。指標遠比數組靈活,但也更危險。
對於同一個char型字串char str[] = "Hellow":
strlen(str)的值為6 ,求的是這個字串有效字元的個數,不包含用於結束字串的 0
sizeof(str)的值為7 , 求的是這個變數一共佔用記憶體的位元組數,當然包含最後一個用於結束字串的 0 。
對於char *p = str ; sizeof(p)的值為int型變數在記憶體中佔用的容量。C++/C 語言沒有辦法知道指標所指的記憶體容量。
注意當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指標。
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 位元組而不是 100 位元組
}
如果函數的參數是一個指標,不要指望用該指標去申請動態記憶體。
GetMemory(str, 200)並沒有使 str 獲得期望的記憶體,str 依舊是 NULL。(pass by value)
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num); //系統會開闢一個臨時儲存區儲存p 的值,這裡的p不是原來那個p pass by value
}
如果非得要用指標參數去申請記憶體,那麼應該改用“指向指標的指標”。
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
char * Test1(void)
{
char str[] = "Hellow!"; //這裡強調不要用 return 語句返回指向“棧記憶體”的指標。因為str是字串,故str會在棧中建立。
return str;
}
char *Test2(void)
{
char *pStr = "Hellow!";//因為 "Hellow!" 是字串,故其會儲存在靜態儲存區,而pStr只是一個指標,根據pass by value , 即使棧中pStr被消除,“Hellow”依舊存在
return pStr;
}
free和delete只是把指標所指的記憶體給釋放掉,但並沒有把指標本身幹掉。如果此時不把 指標 設定為 NULL,會讓人誤以為 p 是個合法的指標。
如果程式比較長,我們有時記不住 p 所指的記憶體是否已經被釋放,在繼續使用 p 之前,通常會用語句 if (p != NULL)進行防錯處理。
(1)指標消亡了,並不表示它所指的記憶體會被自動釋放。
(2)記憶體被釋放了,並不表示指標會消亡或者成了 NULL 指標
“野指標”的成因主要有兩種:
(1)指標變數沒有被初始化。任何指標變數剛被建立時不會自動成為 NULL 指標,它
的預設值是隨機的,它會亂指一氣。
(2)指標 p 被 free 或者 delete 之後,沒有置為 NULL,讓人誤以為 p 是個合法的指標。
(3)指標操作超越了變數的作用範圍。例如:
void Test(void)
{
A *p;
{
A a;
p = &a; // 注意 a 的生命期
}
p->Func(); // p 是“野指標”
}
光用 maloc/free 無法滿足動態對象的要求。對象在建立的同時要自動執行建構函式,對象在消亡之前要自動執行解構函式。由於
malloc/free 是庫函數而不是運算子,不在編譯器控制許可權之內,不能夠把執行建構函式和解構函式的任務強加於 malloc/free 。
因此 C++語言需要一個能完成動態記憶體分配和初始化工作的運算子 new,以及一個能完成清理與釋放記憶體工作的運算子 delete。
注意 new/delete 不是庫函數。
因為 C++程式經常要調用 C 函數,而 C 程式只能用 malloc/free 管理動態記憶體。故C++不把 malloc/free 淘汰。
記憶體耗盡怎麼辦
如果在申請動態記憶體時找不到足夠大的記憶體塊,malloc 和 new 將返回 NULL 指標,
宣告記憶體申請失敗。通常有三種方式處理“記憶體耗盡”問題。
(1)判斷指標是否為 NULL,如果是則馬上用 return 語句終止本函數。
(2)判斷指標是否為 NULL,如果是則馬上用 exit(1)終止整個程式的運行。
(3)為 new 和 malloc 設定異常處理函數。
54頁
C/C++ 記憶體管理問題