總結:
1 棧:爲編譯器自動分配和釋放,如函數參數、局部變量、臨時變量等等
2 堆:爲成員分配和釋放,由程序員本身申請、本身釋放。不然發生內存泄露。典型爲使用new申請的堆內容。
除了這兩部分,還有一部分是:
3 靜態存儲區:內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。它主要存放靜態數據、全局數據和常量。
轉自:
棧內存和堆內存的區別(一個筆試題的一部分)http://blog.csdn.net/richerg85/article/details/19175133
筆試題目:請解釋一個棧內存與一個堆內存的區別,請分析下面代碼運行是否有問題,若是有問題請改正。c++
char* GetMemory(void)程序員
{編程
char p[] = "Hello world";數組
return p;數據結構
}函數
void main(void)優化
{操作系統
char* str = GetMemory();.net
printf(str);指針
}
先看第一個問題:棧內存和堆內存的區別
程序的內存分配
棧(stack):有編譯器自動分配和釋放,存放函數的參數、局部變量、臨時變量、函數返回地址等;
堆(heap):通常有程序員分配和釋放,若是沒有手動釋放,在程序結束時可能由操做系統自動釋放(?這個可能針對Java那樣的有回收機制的語言而說的,對於c/c++,這樣的必需要手動釋放開闢的堆內存),稍有不慎會引發內存泄漏。
2.申請後系統的響應
棧:只要棧的剩餘空間大於所申請的空間,系統將爲程序提供內存,不然將報異常提示棧溢出。
堆:在記錄空閒內存地址的鏈表中尋找一個空間大於所申請空間的堆結點,而後將該結點從空閒結點鏈表中刪除,並將該結點的空間分配給程序。另外,對於大多數系統會在這塊內存空間的首地址出記錄本次分配空間的大小,這樣代碼中的delete 才能正確釋放本內存空間。系統會將多餘的那部分從新空閒鏈表中。
三、申請大小限制
棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的內存的區域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就肯定的常數),若是申請的空間超過棧的剩餘空間時,將提示overflow。所以,能從棧得到的空間較小。
堆:堆是向高地址擴展的數據結構,是不連續的內存區域。這是因爲系統是用鏈表來存儲的空閒內存地址的,天然是不連續的,而鏈表的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬內存。因而可知,堆得到的空間比較靈活,也比較大。
四、分配效率
棧:由系統自動分配,速度較快。但程序員是沒法控制的。
堆:由new分配的內存,通常速度比較慢,並且容易產生內存碎片,不過用起來最方便. 另外,在WINDOWS下,最好的方式是用VirtualAlloc分配內存,不是在堆,也不是在棧是直接在進程的地址空間中保留一快內存,雖然用起來最不方便。可是速度快,也最靈活
五、存儲內容
棧:在棧中,第一個進棧的是主函數下一條指令的地址,而後是函數的各個參數,在大多數編譯器中,參數是由右往左入棧,而後是函數中的局部變量。注意,靜態變量不入棧。出棧則恰好順序相反。
堆:通常在堆的頭部用一個字節存放堆的大小,具體內容由程序員安排。
根據《C++內存管理技術內幕》一書,在C++中,內存分紅5個區,他們分別是堆,棧,自由存續區,全局/靜態存續區,常量存續區。
a) 棧:內存由編譯器在須要時自動分配和釋放。一般用來存儲局部變量和函數參數。(爲運行函數而分配的局部變量、函數參數、返回地址等存放在棧區)。棧運算分配內置於處理器的指令集中,效率很高,可是分配的內存容量有限。
b) 堆:內存使用new進行分配,使用delete或delete[]釋放。若是未能對內存進行正確的釋放,會形成內存泄漏。但在程序結束時,會由操做系統自動回收。
c) 自由存儲區:使用malloc進行分配,使用free進行回收。和堆相似。
d) 全局/靜態存儲區:全局變量和靜態變量被分配到同一塊內存中,C語言中區分初始化和未初始化的,C++中再也不區分了。(全局變量、靜態數據、常量存放在全局數據區)
e) 常量存儲區:存儲常量,不容許被修改。
這裏,在一些資料中是這樣定義C++內存分配的,可編程內存在基本上分爲這樣的幾大部分:靜態存儲區、堆區和棧區。他們的功能不一樣,對他們使用方式也就不一樣。
a)靜態存儲區:內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都存在。它主要存放靜態數據、全局數據和常量。
b)棧區:在執行函數時,函數內局部變量的存儲單元均可以在棧上建立,函數執行結束時這些存儲單元自動被釋放。棧內存分配運算內置於處理器的指令集中,效率很高,可是分配的內存容量有限。
c)堆區:亦稱動態內存分配。程序在運行的時候用malloc或new申請任意大小的內存,程序員本身負責在適當的時候用free或 delete釋放內存。動態內存的生存期能夠由咱們決定,若是咱們不釋放內存,程序將在最後才釋放掉動態內存。 可是,良好的編程習慣是:若是某動態內存再也不使用,須要將其釋放掉,不然,咱們認爲發生了內存泄漏現象。
\
圖3 典型c++內存區域
總結:C++與C語言的內存分配存在一些不一樣,可是總體上就一致的,不會影響程序分析。就C++而言,不論是5部分仍是3大部分,只是分法不一致,將5部分中的c)d)e)合在一塊兒則是3部分的a)。
下面幾段代碼,則會讓你有豁然明白的感受:
void fn()
{
int* p = new int[5];
}
看到new,首先應該想到,咱們分配了一塊堆內存,那麼指針p呢? 它分配的是一塊棧內存,因此這句話的意思就是:在棧內存中存放了一個指向一塊堆內存的指針p。程序會先肯定在堆中分配內存的大小,而後調用 operator new分配內存,而後返回這塊內存的首地址,放入棧中。
注意:這裏爲了簡單並無釋放內存,那麼該怎麼去釋放呢? 是deletep麼? NO,錯了,應該是delete [ ] p,這是告訴編譯器:刪除的是一個數組。
//main.cpp int a = 0; 全局初始化區 char *p1; 全局未初始化區 main() { int b; 棧 char s[] = "abc"; 棧 char *p2; 棧 char *p3 = "123456"; 123456\0在常量區,p3在棧上。 static int c =0; 全局(靜態)初始化區 p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分配得來得10和20字節的區域就在堆區。 strcpy(p1, "123456"); 123456\0放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。 }