1. 程式人生 > >C++ 記憶體管理(c++高質量程式設計)

C++ 記憶體管理(c++高質量程式設計)

一、三種記憶體分配方式

1、靜態儲存區:在程式編譯時分配,這塊記憶體在程式的整個執行期間都存在。例如全域性變數,static變數。

2、在棧上建立分配:在執行函式時,函式內的區域性變數的儲存單元都可以在棧上建立,函式結束時這些單元會自動釋放。棧記憶體分配運算內置於處理器的指令集中,效率高,但是分配的記憶體容量有限。

3、在堆上分配,也叫動態記憶體分配。程式在執行時用malloc或new申請任意大小的空間,程式設計師自己負責在什麼時候free或delete釋放記憶體。生命週期由自己決定,靈活,但問題也多。

常見問題:

1、記憶體分配未成功,卻使用了它。

在使用記憶體之前檢查指標是否為NULL。如果指標p是函式的引數,那麼在函式的入口處用assert(p!=null)進行檢查。如果是用malloc或new來申請的,應用if(p==NULL)或if(p!=NULL)進行防錯處理。

2、記憶體分配成功,但未初始化。

出現錯誤的兩種原因:沒有初始化;誤以為記憶體的預設值全為零,導致引用初值錯誤。

3、記憶體分配成功並初始化,造成越過記憶體邊界。

例如輸出下標多一或少一、for迴圈次數,導致陣列操作越界。

4、忘記記憶體釋放造成的記憶體洩漏。

含有這種錯誤的函式每呼叫一次就丟失一塊記憶體。剛開始時系統記憶體充足,看不到錯誤,終有一次程式會出現錯誤提示:記憶體耗盡。所以動態記憶體的申請和釋放必須配對,程式中malloc與free的使用次數一定要相同,否則會出現錯誤。new/delete同理。

5、釋放了記憶體卻繼續使用它。有以下三種情況:

(1)物件呼叫過於複雜,難以弄清是否釋放,所以應該重新設計資料結構,從根本上解決物件管理的混亂局面。

(2)函式的return語句寫錯,注意不要返回指向“棧記憶體”的指標或引用,因為該記憶體體結束時會自動銷燬。

(3)使用free或delete後,沒有將指標設為NULL,造成野指標。

 

二、陣列和指標

陣列要麼在靜態儲存區被建立(如全域性陣列),要麼在棧上。陣列名對應著(不是指向)一塊記憶體,其地址與容量在生命期內保持不變,只有陣列內容可以改變。

指標可以隨時指向任意型別的記憶體,所以我們常用指標來操作動態記憶體。指標遠比陣列靈活,但也更危險。

1、修改內容

a[]表示的是一個字串變數,它的值可以用下標進行訪問修改;*p指向的是一個字串常量,內容無法修改。

2、複製與比較

不能對陣列名直接複製和比較,應用標準庫函式strcpy和strcmp比較複製。

3、計算記憶體容量

用sizeof算出容量(位元組數)。注意:char a[]="hello";   sizeof(a)=12位元組,但是把陣列作為引數傳遞時,a表示首元素地址,sizeof(a)=4位元組.

三、指標引數傳遞記憶體

如果函式的引數是一個指標,不要用該指標去申請動態記憶體。因為編譯器要為函式的每個引數製作臨時變數,函式體知識修改了臨時副本中的內容。如果非要用指標去申請記憶體,那麼應該用“指向指標的指標”,而且函式的引數是&str。

雙重指標難以理解,用函式的返回值來傳遞動態記憶體。這種方法更加簡單:

用函式返回值傳遞動態記憶體這種方法雖然好用,但是常常有人把return語句用錯。這裡強調不要用return返回指向棧記憶體,因為該記憶體在函式結束時會自動消亡。

 

四。記憶體釋放

1、free、delete只是把指標所指的記憶體釋放掉,並沒有釋放指標本身。通過除錯發現指標的地址補位null,只是變成了野指標,要及時的設定成NULL;

2、函式體內的區域性變數在函式結束時自動消亡。但是:

void fun()

{

    char *p=(char *)malloc(sizeof(char)*100);//動態記憶體會自動釋放麼?

}

(1)指標消亡了,並不代表它所指的記憶體會被自動釋放;

(2)記憶體被釋放了,並不代表指標消亡或成空指標。

 

五、malloc/free和new/delete

malloc與free是c/c++語言的標準庫函式,new/delete是c++運算子。都用於申請動態記憶體和釋放物件。

對於非內部資料型別的物件而言,光用malloc和free無法滿足動態物件的要求。物件在建立的同時要自動執行建構函式,物件消亡之前自動呼叫解構函式。由於malloc/free是庫函式,不是運算子,不在編譯器控制權之內,不能把建構函式和解構函式的任務強加給malloc/free;

因此c++需要一個能完成動態分配和釋放的運算子new/delete。

new/delete 和 malloc/free必須配對使用

六、記憶體耗盡怎麼辦

如果在動態申請記憶體時找不到足夠大的空間,malloc和new將返回NULL指標。通常有三種方法解決:

1、判斷指標是否NULL,如果是則馬上用return終止函式;

2、判斷是否為NULL,如果是用exit(1)終止程式;

3、為new/malloc設定異常處理。

七、malloc/free使用要點

malloc函式原型:void * malloc(size_t size);

我們應該把注意力集中在兩個要素:“型別轉換”“sizeof”。

malloc的返回值為void *型別,所以在呼叫malloc時要顯示的進行型別轉換,換成需要的指標型別。

malloc函式本身並不認識要申請的記憶體是什麼型別,他只關心大小(位元組數)。所以在32位系統和64位系統下要熟記各種基本型別的位元組數。

free函式原型:void free(void * memblock);

八、new/delete使用要點

int *p2=new int[length];使用起來比malloc方便的多,這是因為new內建了sizeof、型別轉換和型別安全檢查功能。對於非內部資料型別而言,new在建立動態物件的同時完成了初始化工作。例如:

Obj *object=new Obj[10];//建立10個動態物件

注意:delete []object;//正確的寫法    如果寫成delete object   表示delete object[0]是,只delete掉了第一個物件。