為啥需要動態分配內存
數組是我們常用的一種數據結構.但它有一個缺點,就是用的時候必須確定數組大小.如果我們要用數組來保存的數據不確定可咋整啊?把數組定得太大浪費空間,太小的話又裝不下.這是一種情況.另外就是對象太大.我們使用的數據大部分時候都默認保存在棧(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.