cpp 編譯器的記憶體分配模式

2021-08-07 09:56:00 字數 4912 閱讀 4726

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

1、棧區(stack)— 程式執行時由編譯器自動分配,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。程式結束時由編譯器自動釋放。

2、堆區(heap) — 在記憶體開闢另一塊儲存區域。一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由os** 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於鍊錶,呵呵。

3、全域性區(靜態區)(static)—編譯器編譯時即分配記憶體。全域性變數和靜態變數的儲存是放在一塊的,初始化的 全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程式結束後由系統釋放

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

5、程式**區—存放函式體的二進位制**。

舉個栗子

nt a = 0; 全域性初始化區

char *p1; 全域性未初始化區

main()

c語言

程式的記憶體分配方式

1.記憶體分配方式

記憶體分配方式有三種:

[1]從靜態存 儲區域分配。內存在程式編譯的時候就已經分配好,這塊內存在程式的整個執行期間都存在。例如全域性變數,static變數。

[2]在棧上建立。 在執行函式時,函式內區域性變數的儲存單元都可以在棧上建立,函式執行結束時這些儲存單元自動被釋放。棧記憶體分配運算內置於處理器的指令集中,效率很高,但 是分配的記憶體容量有限。

[3]從堆上分配,亦稱動態記憶體分配。程式在執行的時候用malloc或new申請任意多少的記憶體,程式設計師自己負責在 何時用free或delete釋放記憶體。動態記憶體的生存期由程式設計師決定,使用非常靈活,但如果在堆上分配了空間,就有責任**它,否則執行的程式會出現內 存洩漏,頻繁地分配和釋放不同大小的堆空間將會產生堆內碎塊。

2.程式的記憶體空間

乙個程式將操作 系統分配給其執行的記憶體塊分為4個區域,如下圖所示。

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

1、棧區(

stack)—

由編譯器自動分配釋放

,存放為執行函式而分配的區域性變數、函式引數、返回資料、返回位址等。其操作方式類似於資料結構中的棧。

2、堆區(heap) —  一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由os** 。分配方式類似於鍊錶。

3、全域性區(靜態區)(static) —存放全域性變數、靜態資料、常量。程式結束後由系統釋放。

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

5、程式**區—存放函式體(類成員函式和全域性函式)的二進位制**。

3.堆與棧的比較

3.1申請方式

stack: 由系統自動分配。 例如,宣告在函式中乙個區域性變數 int b; 系統自動在棧中為b開闢空間。

heap: 需要程式設計師自己申請,並指明大小,在c中malloc函式,c++中是new運算子。

如p1 = (char *)malloc(10); p1 = new char[10];

如p2 = (char *)malloc(10); p2 = new char[20];

但是注意p1、p2本身是在棧中的。

3.2申請後系統的響應

棧:只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則 將報異常提示棧溢位。

堆:首先應該知道作業系統有乙個記錄空閒記憶體位址的鍊錶,當系統收到程式的申請時,會遍歷該鍊錶,尋找第乙個空間大於所 申請空間的堆結點,然後將該結點從空閒結點鍊錶中刪除,並將該結點的空間分配給程式。

對於大多數系統,會在這塊記憶體空間中的首位址處記錄本次 分配的大小,這樣,**中的delete語句才能正確的釋放本記憶體空間。

由於找到的堆結點的大小不一定正好等於申請的大小,系統會自動的將多 餘的那部分重新放入空閒鍊錶中。

3.3申請大小的限制

棧:在windows下,

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

堆:堆是向高位址擴充套件的資料結構,

是不連續的記憶體區域。這是由於系統是用鍊錶來儲存的空閒記憶體位址的,自然是不連續的,而鍊錶的遍歷方向是由低位址向高位址。堆的大小受限於計算機系統中有

效的虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。

3.4申請效率的比較

棧由系統自動分配,速度較快。但 程式設計師是無法控制的。

堆是由new分配的記憶體,一般速度比較慢,而且容易產生記憶體碎片,不過用起來最方便。

另外,在 windows下,最好的方式是用virtualalloc分配記憶體,他不是在堆,也不是棧,而是直接在程序的位址空間中保留一快記憶體,雖然用起來最不方 便。但是速度快,也最靈活。

3.5堆和棧中的儲存內容

棧:在函式呼叫時,第乙個進棧的是主函式中後的下一條指令(函式呼叫語句的 下一條可執行語句)的位址,然後是函式的各個引數,在大多數的c編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。

當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的位址,也就是主函式中的下一條指令,程式由該點繼續執行。

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

3.6訪問效率的比較

char s1 = "a";

char *s2 = "b";

a是在執行時刻賦值的;而b是在編譯時就確定的;但是,在以後的訪問中,在棧上的陣列比 指標所指向的字串(例如堆)快。 比如:

int main()

對應的彙編**

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讀取字元,顯然慢了。

3.7小結

堆和棧的主要區別由以下幾點:

1、管理方式不同;

2、空間大小不同;

3、能否產生碎片不同;

4、生長方向不同;

5、分配方式不同;

6、分配效率不同;

管理方式:對於棧來講,是由編譯器自動管理,無需我們手工控制;對於堆來 說,釋放工作由程式設計師控制,容易產生memory leak。

空間大小:一般來講在32位系統下,堆記憶體可以達到4g的空間,從這個角度來看 堆記憶體幾乎是沒有什麼限制的。但是對於棧來講,一般都是有一定的空間大小的,例如,在vc6下面,預設的棧空間大小是1m。當然,這個值可以修改。

碎片問題:對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而造成大量的碎片,使程式效率降低。對於棧來講,則不會存在這個問 題,因為棧是先進後出的佇列,他們是如此的一一對應,以至於永遠都不可能有乙個記憶體塊從棧中間彈出,在他彈出之前,在他上面的後進的棧內容已經被彈出,詳 細的可以參考資料結構。

生長方向:對於堆來講,生長方向是向上的,也就是向著記憶體位址增加的方向;對於棧來講,它的生長方向是向下的,是向著 記憶體位址減小的方向增長。

分配方式:堆都是動態分配的,沒有靜態分配的堆。棧有2種分配方式:靜態分配和動態分配。靜態分配是編譯器完成的, 比如區域性變數的分配。動態分配由malloca函式進行分配,但是棧的動態分配和堆是不同的,他的動態分配是由編譯器進行釋放,無需我們手工實現。

分配效率:棧是機器系統提供的資料結構,計算機會在底層對棧提供支援:分配專門的暫存器存放棧的位址,壓棧出棧都有專門的指令執行,這就決定了棧的效率 比較高。堆則是c/c++函式庫提供的,它的機制是很複雜的,例如為了分配一塊記憶體,庫函式會按照一定的演算法(具體的演算法可以參考資料結構/作業系統)在 堆記憶體中搜尋可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由於記憶體碎片太多),就有可能呼叫系統功能去增加程式資料段的記憶體空間,這樣就有機會 分 到足夠大小的記憶體,然後進行返回。顯然,堆的效率比棧要低得多。

從這裡我們可以看到,堆和棧相比,由於大量new/delete的使用, 容易造成大量的記憶體碎片;由於沒有專門的系統支援,效率很低;由於可能引發使用者態和核心態的切換,記憶體的申請,代價變得更加昂貴。所以棧在程式中是應用最 廣泛的,就算是函式的呼叫也利用棧去完成,函式呼叫過程中的引數,返回位址, ebp和區域性變數都採用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。

雖然棧有如此眾多的好處,但是由於和堆相比不是那麼靈活,有 時候分配大量的記憶體空間,還是用堆好一些。

無論是堆還是棧,都要防止越界現象的發生(除非你是故意使其越界),因為越界的結果要麼是程式崩 潰,要麼是摧毀程式的堆、棧結構,產生以想不到的結果。

4.new/delete與malloc/free比較

從c++角度上說,使用new分配堆空間可以呼叫類的建構函式,而malloc()函式僅僅是乙個函式呼叫,它不會呼叫建構函式,它所接受的引數是乙個 unsigned long型別。同樣,delete在釋放堆空間之前會呼叫析構函式,而free函式則不會。

class time{

public:

time(int,int,int,string);

~time(){

cout<<"call time』s destructor by:"<

編譯器對變數的記憶體分配方式

本文轉至 今天看到 c陷阱與缺陷 書中的一段 int i,a 10 for i 1 i 10 i a i 0 書中說 在for語句的比較部分本來是i 10 卻寫成了i 10 因此實際上並不存在的a 10 被設定為0,也就是內存在陣列a之後的乙個字 word 的記憶體被設定為0。如果用來編譯這段程式的...

編譯器選項 Zm(指定預編譯頭的記憶體分配限制)

zmnumber其中 number 比例因子,用於為預編譯頭確定 編譯器的記憶體分配限制。備註 zmnumber 為預編譯頭確定編譯器的記憶體分配限制。number 是比例因子,其預設值為 100,指定分配 50mb 記憶體。number 的最大值為 2000。下表說明 number 如何修改記憶體...

變數的記憶體單元是編譯器在編譯時候分配的

所謂在編譯期間分配空間指的是靜態分配空間 相對於用new動態申請空間 如全域性變數或靜態變數 包括一些複雜型別的常量 它們所需要的空間大小可以明確計算出來,並且不會再改變,因此它們可以直接存放在可執行檔案的特定的節裡 而且包含初始化的值 程式執行時也是直接將這個節載入到特定的段中,不必在程式執行期間...