版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。 https://blog.csdn.net/zhaobryant/article/details/42010029
一、程式的記憶體配置設定
對于一個由C/C++編譯的程式,其所占用的記憶體可以劃分為以下幾個部分:
- 棧區(stack)—— 由作業系統自動配置設定和釋放,主要用于存放函數參數值,局部變量等。其操作方式類似于資料結構中的棧。
- 堆區(heap)—— 一般由程式員動态配置設定和釋放,若程式員不主動釋放,則程式結束後由作業系統回收。注意,它與資料結構中的堆是不同的,配置設定方式類似于連結清單。
- BSS段——主要用于存放未初始化的靜态變量和全局變量,可讀寫,它在程式結束後由作業系統進行釋放。
- 資料段(data)——主要用于存放已初始化的靜态變量和全局變量,可讀寫,它在程式結束後由作業系統釋放。
- 代碼段(text)——主要用于儲存程式代碼,包括CPU執行的機器指令,同時全局常量也是儲存在代碼段的,如字元串字面值。
二、程式執行個體
/main.cpp
int a = 0; // 全局初始化區域
char *p1; // 全局未初始化區域
int main(){
int b; // 棧
char s[] = "adoryn"; // 棧
char *p2; // 棧
char *p3 = "zhaobryant"; // 字元串字面量存放在常量區,p3存放在棧上
static int c = 0; // 全局(靜态)初始化區域
p1 = (char *)malloc(10);
p2 = (char *)malloc(20); // 配置設定獲得的10和20位元組的記憶體區放在堆區
strcpy(p1, "zhaobryant"); // 字元串字面量存放在常量區,編譯器可能會将它與p3所指向的"zhaobryant"優化為同一個位址
return 0;
}
三、堆和棧的理論知識
1. 申請方式對比
棧stack:
由系統自動配置設定。例如,聲明在函數中一個局部變量,即int b,系統自動在棧中為變量b開辟空間。
堆heap:
需要程式員自己申請,并指明大小。
在C中使用malloc函數,如p1 = (char *)malloc(10)
在C++中用new運算符,如p2 = new char[10]
但是p1、p2本身是在棧中的。
2. 申請後系統響應
隻要棧的剩餘空間大于所申請空間,系統将為程式提供記憶體,否則将傳回異常提示棧溢出。
首先應該知道作業系統有一個記錄空閑記憶體塊節點的連結清單,當系統收到程式的申請時,會周遊該連結清單,尋找第一個空間大于所申請空間的記憶體塊節點,然後将該節點從空閑節點連結清單中删除,并将該節點的空間配置設定給程式。另外,對于大多數系統,會在這塊記憶體空間中的首位址處記錄本次配置設定的大小,這樣,代碼中的delete語句才能正确的釋放記憶體空間。另外,由于找到的空閑記憶體塊節點的大小不一定正好等于申請的大小,系統會自動的将多餘的那部分重新放入空閑連結清單中。
3. 申請大小的限制
在Windows下,棧是向低位址擴充的資料結構,是一塊連續的記憶體的區域。也就是說,棧頂的位址和棧的最大容量是系統預先規定好的。在Windows下,棧的大小是2M(也有的說是1M,總之是一個編譯時就确定的常數),如果申請的空間超過棧的剩餘空間時,将提示記憶體溢出。是以,能從棧獲得的空間較小。
堆是向高位址擴充的資料結構,是不連續的記憶體區域。這是由于系統是用連結清單來存儲空閑記憶體位址的,自然是不連續的,而連結清單的周遊方向是由低位址向高位址。堆的大小受限于計算機系統中有效的虛拟記憶體。由此可見,堆獲得的空間比較靈活,也比較大。
4. 申請效率對比
棧由系統自動配置設定,速度較快,但程式員是無法控制的。
堆是由new/malloc進行記憶體配置設定,一般速度比較慢,且容易産生記憶體碎片,不過用起來最友善,速度快,也最靈活。
5. 存儲内容對比
在函數調用時,第一個進棧的是主函數中後的下一條指令(函數調用語句的下一條可執行語句)的位址,然後是函數的各個參數,在大多數的C編譯器中,參數是由右往左入棧的,然後是函數中的局部變量。注意靜态變量是不入棧的。當本次函數調用結束後,局部變量先出棧,然後是參數,最後棧頂指針指向最開始存的地 址,也就是主函數中的下一條指令,程式由該點繼續運作。
一般是在堆的頭部用一個位元組存放堆的大小,堆中的具體内容由程式員安排。
6. 存取效率對比
對比兩段代碼:
char s1[] = "aaaaaaaaaaaaaaa";
char *s2 = "bbbbbbbbbbbbbbbbb";
如上,aaaaaaaaaaa是在運作時刻指派的;而bbbbbbbbbbb是在編譯時就确定的。
但是,在以後的存取中,在棧上的數組比指針所指向的字元串快。
例如:
#include ...
int main(){
char a = 1;
char c[] = "1234567890";
char *p = "1234567890";
a = c[1];
a = p[1];
return 0;
}
對應的彙編代碼:
10: a = c[1];
00401067 8A 4D F1 mov c1, byte ptr [ebp-0Fh]
0040106A 88 4D FC mov byte ptr [ebp-4], c1
11: a = p[1];
0040106D 8B 55 EC mov edx, dword ptr [ebp-14h]
0040106D 8A 42 01 mov a1, byte ptr [edx+1]
00401073 88 45 FC mov byte ptr [ebp-4], a1
第一種在讀取時直接就把字元串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,再根據edx讀取字元,顯然慢了。
7. 小結
堆和棧的差別可以用如下的比喻來看出:
- 使用棧就像我們去飯館裡吃飯,隻管點菜(發出申請)、付錢、和吃(使用),吃飽了就走,不必理會切菜、洗菜等準備工作和洗碗、刷鍋等掃尾工作,其好處是快捷簡單,但是自由度小。
- 使用堆就象是自己動手做喜歡吃的菜肴,比較麻煩,但是比較符合自己的口味,而且自由度大。