為啥需要動態分配記憶體
數組是我們常用的一種資料結構.但它有一個缺點,就是用的時候必須確定數組大小.如果我們要用數組來儲存的資料不確定可咋整啊?把數組定得太大浪費空間,太小的話又裝不下.這是一種情況.另外就是對象太大.我們使用的資料大部分時候都預設儲存在棧(stack)裡面,由系統去管理,會自動給分配記憶體,自動給刪除掉.但是stack很小,就那麼幾M,如果你讀取一個幾十M的常值內容然後儲存到一個字串裡,stack肯定會被撐爆了.
上面說的是兩種最常見的兩種情景.另外如果你想準確的控制記憶體的釋放.比如記憶體比較緊缺,你用完一塊記憶體後就想立馬釋放掉.如果系統自動去釋放的話可能得等到變數生命週期結束時再釋放.不會做得到立馬釋放.
基於上面等一些原因於是出現了堆(heap),這是由使用者自己控制的一片記憶體區,比stack大多了.你可以自由的在裡面申請空間釋放空間.
C語言中的動態記憶體分配
C語言是比較接近底層的,用它舉例說動態記憶體的分配更容易理解.C++是作了一定程度的封裝.就以數組來舉例吧.假如你不知道要使用的資料具體是多少,只要啟動並執行時候才知道.就用N表示需要N個int類型的空間.於是我們需要動態分配一塊記憶體,並用一個int型指標指向記憶體的首地址.
int n = 123; //這裡隨便賦個值,實際使用時可能是傳個參數確定它的值
int * p = (int*) malloc(sizeof(int)*n);
/*malloc是一個庫函數,調用它去申請記憶體空間,它的傳回值是void*指標,所以需要做下類型轉換變成int*指標.它的參數是記憶體大小,以位元組為單位,表示要申請多少個位元組.int類型可能在不同的系統裡佔用的位元組不一樣,所以用sizeof計算下先. 假如你要給結構體分配記憶體的話,假如有結構體struct test則是,struct test * pTest = (struct test*) malloc(sizeof(struct test));*/
現在可以把弄來的記憶體當數組用了.實際上也不是真的數組,只是類比拉.
int * pArray = p; //用另一個指標來指向p,因為p要保留著記憶體的首地址,這樣後面釋放記憶體的時候才會正確釋放
*pArray = 123;
*(pArray + 1) = 456;
free(p); //用完了就可以這樣來釋放記憶體
p = 0; //讓指標指向一塊空記憶體,這樣的好處有,比如你不小心在哪又再free下p就會出錯,但讓p指向空記憶體後多次重複free也不會出錯.
//free(p)不是釋放p指標的記憶體,而是以它為首地址後面的一大塊.它自己儲存的地址值還是一直在那,所以你free完了後再列印p儲存的地址值還跟以前一樣.
C++動態記憶體分配
C++中多了個class的概念,而類裡面有個比較重要的概念是建構函式.而建構函式不能手動去調用,是執行個體化類時自動調用.如果像C一樣用malloc來給某個類動態分配一塊記憶體的話,這個類就不會調用到建構函式了.於是C++裡出現了個關鍵字new,當你使用new動態一塊記憶體時會自動調用建構函式(這具體咋實現的就不知道了啊.反正最後封裝成一個new給我們用).用完了釋放的話就用delete,此時會調用解構函式.
舉個例子吧假如有類Arwen
Class Arwen
{
public:
Arwen(string str){ name = str;}
string name;
~Arwen(){ };
}
Arwen weiwen("csharp"); //這樣執行個體化一個類,是由系統在stack中分配記憶體並釋放記憶體不用我們管
Arwen* weiwenhp = new Arwen("cplusplus"); //必須用指標Arwen*,這樣才是動態記憶體分配,由使用者自己去申請空間去釋放空間.
delete weiwenhp; //釋放記憶體
記憶體泄露
動態分配記憶體時最容易犯的錯,也是最不容易發現的就是記憶體泄露了啊.
嚴格來講內泄露不是一種錯誤,它只是沒有釋放掉申請來的記憶體,造成了浪費而已.其實很容易用這一點來做一個病毒.你就不停的去申請記憶體,但都不給釋放.到最後記憶體就會被耗光了.
研究記憶體泄露是個比較複雜的話題了,會有很多種情況會導致泄露,也有很多方法去防範.
舉幾個簡單的例子瞧下
int * p = new int[88];
delete p; //這裡就記憶體泄露了,要用delete []p才行.在C中就free(p)就行了
另外在函數中delete還沒執行到就退出了也容易記憶體泄露,比如
int function(int num)
{
int *p = new[44];
if( num > 111)
return 0;
delete []p;
return 1;
}
如果num大於100,執行到return 0時就退出了,不會執行到delete.