在c++中內存主要分為5個存儲區:
棧(Stack):局部變量,函數參數等存儲在該區,由編譯器自動分配和釋放.棧屬於計算機系統的數據結構,進棧出棧有相應的計算機指令支持,而且分配專門的寄存器存儲棧的地址,效率分高,內存空間是連續的,但棧的內存空間有限。
堆(Heap):需要程序員手動分配和釋放(new,delete),屬於動態分配方式。內存空間幾乎沒有限制,內存空間不連續,因此會產生內存碎片。操作系統有一個記錄空間內存的鏈表,當收到內存申請時遍歷鏈表,找到第一個空間大於申請空間的堆節點,將該節點分配給程序,並將該節點從鏈表中刪除。一般,系統會在該內存空間的首地址處記錄本次分配的內存大小,用於delete釋放該內存空間。
自由存儲區:和堆類似,有malloc,free分配和釋放內存空間
全局/靜態存儲區:全局變量,靜態變量分配到該區,到程序結束時自動釋放
常量存儲區:存放常量,而且不允許修改
使用存儲區的三種方式:
1)靜態存儲區(Static Memory)
全局變量,靜態變量及靜態類成員存儲在該區,在編譯期間就進行分配,生存期到程序結束。存儲在該區的對象只初始化一次,且在程序運行期間地址固定不變。
2)自動存儲區(Autormatic Memory)
局部變量,函數參數等存儲在該區,由編譯器自動分配和釋放
3)自由存儲區(Free Store)
由程序員手動分配和釋放內存(new,delete)
堆和棧的區別:
1)空間大小:棧的內存空間是連續的,空間大小通常是系統預先規定好的,即棧頂地址和最大空間是確定的;而堆得內存空間是不連續的,由一個記錄空間空間的鏈表負責管理,因此內存空間幾乎沒有限制,在32位系統下,內存空間大小可達到4G
2)管理方式:棧由編譯器自動分配和釋放,而堆需要程序員來手動分配和釋放,若忘記delete,容易產生內存洩漏。
3)生長方向不同:對於棧,他是向著內存地址減小的方向生長的,這也是為什麼棧的內存空間是有限的;而堆是向著內存地址增大的方向生長的
4)碎片問題:由於棧的內存空間是連續的,先進後出的方式保證不會產生零碎的空間;而堆分配方式是每次在空閒鏈表中遍歷到第一個大於申請空間的節點,每次分配的空間大小一般不會正好等於申請的內存大小,頻繁的new操作勢必會產生大量的空間碎片
5)分配效率:棧屬於機器系統提供的數據結構,計算機會在底層對棧提供支持,出棧進棧由專門的指令執行,因此效率較高。而堆是c/c++函數庫提供的,當申請空間時需要按照一定的算法搜索足夠大小的內存空間,當沒有足夠的空間時,還需要額外的處理,因此效率較低。
使用內存時幾點注意事項:
1)用new和malloc申請內存時,在使用前要檢查內存是否分配成功
char *p=new char[10];
if(p==NULL)
return;
2)使用內存之前要進行初始化
3)在對內存進行操作時,防止越界,如數組操作要注意下標范圍
4)對於動態分配的內存,一定要手動釋放,否則程序每運行一次就會丟失一部分內存,造成內存洩漏
5)防止內存釋放後繼續使用它,主要有以下三種情況:
a.程序中的對象調用關系過於復雜,實在難以搞清楚某個對象究竟是否已經釋放了內存,此時應該重新設計數據結構,從根本上解決對象管理的混亂局面。
b.函數的return語句寫錯了,注意不要返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數體結束時被自動銷毀。
c.使用free或delete釋放了內存後,沒有將指針設置為NULL。導致產生“野指針”。
野指針:“野指針”不是NULL指針,是指向“垃圾”內存的指針。人們一般不會錯用NULL指針,因為用if語句很容易判斷。但是“野指針”是很危險的,if語句對它不起作用。
“野指針”的成因主要有三種:
(a)指針變量沒有被初始化。任何指針變量剛被創建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創建的同時應當被初始化,要麼將指針設置為NULL,要麼讓它指向合法的內存。
char *p; //此時p為野指針
(b)指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針.
char *p=new char[10]; //指向堆中分配的內存首地址
cin>> p;
delete []p; //p重新變為野指針
(c)指針操作超越了變量的作用范圍。
char *p=new char[10]; //指向堆中分配的內存首地址
cin>> p;
cout<<*(p+10); //可能輸出未知數據
6)指針的注意點:
a.指針指向常量存儲區對象
char *p="abc";
此時p指向的是一個字符串常量,不能對*p的內容進行寫操作,如srtcpy(p,s)是錯誤的,因為p的內容為“abc”字符串常量,該數據存儲在常量存儲區,但可以對指針p進行操作,讓其指向其他的內存空間。
b.資源洩漏
char *p=new char[3]; //分配三個字符空間,p指向該內存空間
p="ab"; //此時p指向常量“ab”,而不再是new char分配的內存空間了,從而造成了資源洩漏
delete []p; //釋放時報錯
c.內存越界
char *p=new char[3]; //分配三個字符空間,p指向該內存空間
strcpy(p,"abcd"); //將abcd存處在分配的內存空間中,由於strlen("abcd")=4>3,越界
delete []p; //釋放時出錯
注:p="ab"和strcpy(p,"ab"),含義不一樣,前者指針p指向常量“ab”存儲區域的首地址,改變了p最開始指向的new申請的內存空間;而後者是將“ab”分配到new申請的內存空間中;