一、預備知識—程序的内存分(fēn)配 一個由c/C++編譯的程序占用(yòng)的内存分(fēn)為(wèi)以下幾個部分(fēn) 1、棧區(qū)(stack)— 由編譯器自動分(fēn)配釋放 ,存放函數的參數值,局部變量的值等。其操作(zuò)方式類似于數據結構中(zhōng)的棧。 2、堆區(qū)(heap) — 一般由程序員分(fēn)配釋放, 若程序員不釋放,程序結束時可(kě)能(néng)由OS回收 。注意它與數據結構中(zhōng)的堆是兩回事,分(fēn)配方式倒是類似于鏈表,呵呵。 3、全局區(qū)(靜态區(qū))(static)—,全局變量和靜态變量的存儲是放在一塊的,初始化的全局變量和靜态變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜态變量在相鄰的另一塊區(qū)域。 - 程序結束後有(yǒu)系統釋放 4、文(wén)字常量區(qū) —常量字符串就是放在這裏的。 程序結束後由系統釋放 5、程序代碼區(qū)—存放函數體(tǐ)的二進制代碼。
二、例子程序 新(xīn)疆軟件開發網 這是一個前輩寫的,非常詳細 //main.cpp int a = 0; 全局初始化區(qū) char *p1; 全局未初始化區(qū) main() { int b; 棧 char s[] = "abc"; 棧 char *p2; 棧 char *p3 = "123456"; 123456\0在常量區(qū),p3在棧上。 static int c =0; 全局(靜态)初始化區(qū) p1 = (char *)malloc(10); p2 = (char *)malloc(20); 分(fēn)配得來得10和20字節的區(qū)域就在堆區(qū)。 strcpy(p1, "123456"); 123456\0放在常量區(qū),編譯器可(kě)能(néng)會将它與p3所指向的"123456"優化成一個地方。 } 二、堆和棧的理(lǐ)論知識 2.1申請方式 stack: 由系統自動分(fēn)配。 例如,聲明在函數中(zhōng)一個局部變量 int b; 系統自動在棧中(zhōng)為(wèi)b開辟空間 heap: 需要程序員自己申請,并指明大小(xiǎo),在c中(zhōng)malloc函數 如p1 = (char *)malloc(10); 在C++中(zhōng)用(yòng)new運算符 如p2 = (char *)malloc(10); 但是注意p1、p2本身是在棧中(zhōng)的。 2.2 申請後系統的響應 棧:隻要棧的剩餘空間大于所申請空間,系統将為(wèi)程序提供内存,否則将報異常提示棧溢出。 堆:首先應該知道操作(zuò)系統有(yǒu)一個記錄空閑内存地址的鏈表,當系統收到程序的申請時, 會遍曆該鏈表,尋找第一個空間大于所申請空間的堆結點,然後将該結點從空閑結點鏈表中(zhōng)删除,并将該結點的空間分(fēn)配給程序,另外,對于大多(duō)數系統,會在這塊内存空間中(zhōng)的首地址處記錄本次分(fēn)配的大小(xiǎo),這樣,代碼中(zhōng)的delete語句才能(néng)正确的釋放本内存空間。另外,由于找到的堆結點的大小(xiǎo)不一定正好等于申請的大小(xiǎo),系統會自動的将多(duō)餘的那部分(fēn)重新(xīn)放入空閑鏈表中(zhōng)。 2.3申請大小(xiǎo)的限制 棧:在Windows下,棧是向低地址擴展的數據結構,是一塊連續的内存的區(qū)域。這句話的意思是棧頂的地址和棧的最大容量是系統預先規定好的,在 WINDOWS下,棧的大小(xiǎo)是2M(也有(yǒu)的說是1M,總之是一個編譯時就确定的常數),如果申請的空間超過棧的剩餘空間時,将提示overflow。因此,能(néng)從棧獲得的空間較小(xiǎo)。 堆:堆是向高地址擴展的數據結構,是不連續的内存區(qū)域。這是由于系統是用(yòng)鏈表來存儲的空閑内存地址的,自然是不連續的,而鏈表的遍曆方向是由低地址向高地址。堆的大小(xiǎo)受限于計算機系統中(zhōng)有(yǒu)效的虛拟内存。由此可(kě)見,堆獲得的空間比較靈活,也比較大。 2.4申請效率的比較: 棧由系統自動分(fēn)配,速度較快。但程序員是無法控制的。 堆是由new分(fēn)配的内存,一般速度比較慢,而且容易産(chǎn)生内存碎片,不過用(yòng)起來最方便. 另外,在WINDOWS下,最好的方式是用(yòng)VirtualAlloc分(fēn)配内存,他(tā)不是在堆,也不是在棧是直接在進程的地址空間中(zhōng)保留一快内存,雖然用(yòng)起來最不方便。但是速度快,也最靈活 2.5堆和棧中(zhōng)的存儲内容 棧: 在函數調用(yòng)時,第一個進棧的是主函數中(zhōng)後的下一條指令(函數調用(yòng)語句的下一條可(kě)執行語句)的地址,然後是函數的各個參數,在大多(duō)數的C編譯器中(zhōng),參數是由右往左入棧的,然後是函數中(zhōng)的局部變量。注意靜态變量是不入棧的。 當本次函數調用(yòng)結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地址,也就是主函數中(zhōng)的下一條指令,程序由該點繼續運行。 堆:一般是在堆的頭部用(yòng)一個字節存放堆的大小(xiǎo)。堆中(zhōng)的具(jù)體(tǐ)内容有(yǒu)程序員安(ān)排。 2.6存取效率的比較
char s1[] = "aaaaaaaaaaaaaaa"; char *s2 = "bbbbbbbbbbbbbbbbb"; aaaaaaaaaaa是在運行時刻賦值的; 而bbbbbbbbbbb是在編譯時就确定的; 但是,在以後的存取中(zhōng),在棧上的數組比指針所指向的字符串(例如堆)快。 比如: #include <stdio.h>; 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 第一種在讀取時直接就把字符串中(zhōng)的元素讀到寄存器cl中(zhōng),而第二種則要先把指針值讀到edx中(zhōng),在根據edx讀取字符,顯然慢了。 ?
2.7小(xiǎo)結: 堆和棧的區(qū)别可(kě)以用(yòng)如下的比喻來看出: 使用(yòng)棧就象我們去飯館裏吃飯,隻管點菜(發出申請)、付錢、和吃(使用(yòng)),吃飽了就走,不必理(lǐ)會切菜、洗菜等準備工(gōng)作(zuò)和洗碗、刷鍋等掃尾工(gōng)作(zuò),他(tā)的好處是快捷,但是自由度小(xiǎo)。 使用(yòng)堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。 堆和棧的區(qū)别主要分(fēn): 操作(zuò)系統方面的堆和棧,如上面說的那些,不多(duō)說了。 還有(yǒu)就是數據結構方面的堆和棧,這些都是不同的概念。這裏的堆實際上指的就是(滿足堆性質(zhì)的)優先隊列的一種數據結構,第1個元素有(yǒu)最高的優先權;棧實際上就是滿足先進後出的性質(zhì)的數學(xué)或數據結構。 雖然堆棧,堆棧的說法是連起來叫,但是他(tā)們還是有(yǒu)很(hěn)大區(qū)别的,連着叫隻是由于曆史的原因。
|