天天看點

堆記憶體和棧記憶體詳解

堆:順序随意

棧:先進後出

堆和棧的差別

一、預備知識—程式的記憶體配置設定

一個由c/C++編譯的程式占用的記憶體分為以下幾個部分

1、棧區(stack)— 由編譯器自動配置設定釋放 ,存放函數的參數值,局部變量的值等。其操作方式類似于資料結構中的棧

2、堆區(heap) — 一般由程式員配置設定釋放, 若程式員不釋放,程式結束時可能由OS回收 。注意它與資料結構中的堆是兩回事,配置設定方式倒是類似于連結清單,呵呵。

3、全局區(靜态區)(static)—,全局變量和靜态變量的存儲是放在一塊的,初始化的全局變量和靜态變量在一塊區域, 未初始化的全局變量和未初始化的靜态變量在相鄰的另一塊區域。 - 程式結束後有系統釋放

4、文字常量區 —常量字元串就是放在這裡的。 程式結束後由系統釋放  

5、程式代碼區—存放函數體的二進制代碼。

二、例子程式

這是一個前輩寫的,非常詳細

//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"優化成一個地方。  

}  

二、堆和棧的理論知識

2.1申請方式

stack:由系統自動配置設定。 例如,聲明在函數中一個局部變量 int b; 系統自動在棧中為b開辟空間

heap:需要程式員自己申請,并指明大小,在c中malloc函數如p1 = (char *)malloc(10);在C++中用new運算符如p2 = (char *)malloc(10);但是注意p1、p2本身是在棧中的

2.2申請後系統的響應

棧:隻要棧的剩餘空間大于所申請空間,系統将為程式提供記憶體,否則将報異常提示棧溢出。

堆: 首先應該知道作業系統有一個記錄空閑記憶體位址的連結清單,當系統收到程式的申請時,會周遊該連結清單,尋找第一個空間大于所申請空間的堆結點,然後将該結點從空閑 結點連結清單中删除,并将該結點的空間配置設定給程式,另外,對于大多數系統,會在這塊記憶體空間中的首位址處記錄本次配置設定的大小,這樣,代碼中的delete語句 才能正确的釋放本記憶體空間。另外,由于找到的堆結點的大小不一定正好等于申請的大小,系統會自動的将多餘的那部分重新放入空閑連結清單中。  

2.3申請大小的限制

棧: 在Windows下,棧是向低位址擴充的資料結構,是一塊連續的記憶體的區域。這句話的意思是棧頂的位址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小是2M(也有的說是1M,總之是一個編譯時就确定的常數),如果申請的空間超過棧的剩餘空間時,将提示overflow。因 此,能從棧獲得的空間較小。

堆:堆是向高位址擴充的資料結構,是不連續的記憶體區域。這是由于系統是用連結清單來存儲的空閑記憶體位址的,自然是不連續的,而連結清單的周遊方向是由低位址向高位址。堆的大小受限于計算機系統中有效的虛拟記憶體。由此可見,堆獲得的空間比較靈活,也比較大。  

2.4申請效率的比較:

棧由系統自動配置設定,速度較快。但程式員是無法控制的。

堆 是由new配置設定的記憶體,一般速度比較慢,而且容易産生記憶體碎片,不過用起來最友善. 另外,在WINDOWS下,最好的方式是用VirtualAlloc配置設定記憶體,他不是在堆,也不是在棧是直接在程序的位址空間中保留一快記憶體,雖然用起來 最不友善。但是速度快,也最靈活  

2.5堆和棧中的存儲内容

棧: 在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的位址,然後是函數的各個參數,在大多數的C編譯器中,參數是由 右往左入棧的,然後是函數中的局部變量。注意靜态變量是不入棧的。當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的位址, 也就是主函數中的下一條指令,程式由該點繼續運作。  

堆:一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體内容有程式員安排。  

2.6存取效率的比較  

char s1[] = "aaaaaaaaaaaaaaa";  

char *s2 = "bbbbbbbbbbbbbbbbb";  

aaaaaaaaaaa是在運作時刻指派的;而bbbbbbbbbbb是在編譯時就确定的;但是,在以後的存取中,在棧上的數組比指針所指向的字元串(例如堆)快。  

比如:

#include

void main()

     char a = 1;      

     char c[] = "1234567890";      

     char *p ="1234567890";     

     a = c[1];      

     a = p[1];      

     return;      

對應的彙編代碼  

10: a = c[1];  

00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]

0040106A 88 4D FC mov byte ptr [ebp-4],cl  

11: a = p[1];  

0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]  

00401070 8A 42 01 mov al,byte ptr [edx+1]  

00401073 88 45 FC mov byte ptr [ebp-4],al  

第一種在讀取時直接就把字元串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字元,顯然慢了。  

2.7小結:

堆 和棧的差別可以用如下的比喻來看出:使用棧就象我們去飯館裡吃飯,隻管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作 和洗碗、刷鍋等掃尾工作,他的好處是快捷,但是自由度小。 使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。  

堆和棧的差別主要分:  

操 作系統方面的堆和棧,如上面說的那些,不多說了。還有就是資料結構方面的堆和棧,這些都是不同的概念。這裡的堆實際上指的就是(滿足堆性質的)優先隊列的 一種資料結構,第1個元素有最高的優先權;棧實際上就是滿足先進後出的性質的數學或資料結構。雖然堆棧,堆棧的說法是連起來叫,但是他們還是有很大差別 的,連着叫隻是由于曆史的原因。

=======================================

---------------------------------------

轉一篇文章:

可 能許多人對記憶體配置設定上的“棧 stack”和“堆 heap”還不是很明白。包括一些科班出身的人也不明白這兩個概念。我不想過多的說這兩個東西。簡單的來講,stack上配置設定的記憶體系統自動釋 放,heap上配置設定的記憶體,系統不釋放,哪怕程式退出,那一塊記憶體還是在那裡。stack一般是靜态配置設定記憶體,heap上一般是動态配置設定記憶體。 

由 malloc系統函數配置設定的記憶體就是從堆上配置設定記憶體。從堆上配置設定的記憶體一定要自己釋放。用free釋放,不然就是術語——“記憶體洩露”(或是“記憶體漏 洞”)—— Memory Leak。于是,系統的可配置設定記憶體會随malloc越來越少,直到系統崩潰。還是來看看“棧記憶體”和“堆記憶體”的差别吧。 

棧記憶體配置設定 

—————-------------------------------------------------- 

char* 

AllocStrFromStack() 

char pstr[100]; 

return pstr; 

堆記憶體配置設定 

—————--------------------------------------------------char* 

AllocStrFromHeap(int len) 

char *pstr; 

if ( len <= 0 ) return NULL; 

return ( char* ) malloc( len ); 

對 于第一個函數,那塊pstr的記憶體在函數傳回時就被系統釋放了。于是所傳回的char*什麼也沒有。而對于第二個函數,是從堆上配置設定記憶體,是以哪怕是程式 退出時,也不釋放,是以第二個函數的傳回的記憶體沒有問題,可以被使用。但一定要調用free釋放,不然就是Memory Leak! 

在堆上配置設定記憶體很容易造成記憶體洩漏,這是C/C++的最大的“克星”,如果你的程式要穩定,那麼就不要出現Memory Leak。是以,我還是要在這裡千叮咛萬囑付,在使用malloc系統函數(包括calloc,realloc)時千萬要小心。 

記 得有一個UNIX上的服務應用程式,大約有幾百的C檔案編譯而成,運作測試良好,等使用時,每隔三個月系統就是down一次,搞得許多人焦頭爛額,查不出 問題所在。隻好,每隔兩個月人工手動重新開機系統一次。出現這種問題就是Memery Leak在做怪了,在C/C++中這種問題總是會發生,是以你一定要小心。

我保證,做過許多C/C++的工程的程式員,都會對malloc或是new有些感冒。當你什麼時候在使用malloc和new時,有一種輕度的緊張和惶恐的感覺時,你就具備了這方面的修養了。 

對于malloc和free的操作有以下規則: 

1) 配對使用,有一個malloc,就應該有一個free。(C++中對應為new和delete) 

2) 盡量在同一層上使用,不要像上面那種,malloc在函數中,而free在函數外。最好在同一調用層上使用這兩個函數。 

3) malloc配置設定的記憶體一定要初始化。free後的指針一定要設定為NULL。 

注: 雖然現在的作業系統(如:UNIX和Win2k/NT)都有程序記憶體跟蹤機制,也就是如果你有沒有釋放的記憶體,作業系統會幫你釋放。但作業系統依然不會釋 放你程式中所有産生了Memory Leak的記憶體,是以,最好還是你自己來做這個工作。(有的時候不知不覺就出現Memory Leak了,而且在幾百萬行的代碼中找無異于海底撈針,Rational有一個工具叫Purify,可能很好的幫你檢查程式中的Memory Leak)

繼續閱讀