堆和棧

一直以來總是對這個問題的認識比較朦朧,我相信很多朋友也是這樣的,總是聽到內存一會在棧上分配,一會又在堆上分配,那麼它們之間到底是怎麼的區別呢?爲了說明這個問題,我們先來看一下內存內部的組織情況.

這裏寫圖片描述
從上圖可知,程序佔用的內存被分了以下幾部分.

1、棧區(stack)

由編譯器自動分配釋放 ,存放函數的參數值,局部變量的值等,內存的分配是連續的,類似於平時我們所說的棧,如果還不清楚,那麼就把它想成數組,它的內存分配是連續分配的,即,所分配的內存是在一塊連續的內存區域內.當我們聲明變量時,那麼編譯器會自動接着當前棧區的結尾來分配內存.

2、堆區(heap)

一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由操作系統回收.類似於鏈表,在內存中的分佈不是連續的,它們是不同區域的內存塊通過指針鏈接起來的.一旦某一節點從鏈中斷開,我們要人爲的把所斷開的節點從內存中釋放.

3、全局區(靜態區)(static)

全局變量和靜態變量的存儲是放在一塊的,初始化的全局變量和靜態變量在一塊區域, 未初始化的全局變量和未初始化的靜態變量在相鄰的另一塊區域。 程序結束後由系統釋放

4、文字常量區

常量字符串就是放在這裏的。 程序結束後由系統釋放

5、程序代碼區

存放函數體的二進制代碼。

先看一個例子.

char c; //棧上分配
char *p = new char[3]; //堆上分配,將地址賦給了p;
  • 在 編譯器遇到第一條指令時,計算其大小,然後去查找當前棧的空間是大於所需分配的空間大小,如果這時棧內空間大於所申請的空間,那麼就爲其分配內存空間,注 意:在這裏,內在空間的分配是連續的,是接着上次分配結束後進行分配的.如果棧內空間小於所申請的空間大小,那麼這時系統將揭示棧溢出,並給出相應的異常 信息.

  • 編譯器遇到第二條指令時,由於p是在棧上分配的,所以在爲p分配內在空間時和上面的方法一樣,但當遇到new關 鍵字,那麼編譯器都知道,這是用戶申請的動態內存空間,所以就會轉到堆上去爲其尋找空間分配.大家注意:堆上的內存空間不是連續的,它是由相應的鏈表將其 空間區時的內在區塊連接的,所以在接到分配內存空間的指定後,它不會馬上爲其分配相應的空間,而是先要計算所需空間,然後再到遍列整個堆(即遍列整個鏈的 節點),將第一次遇到的內存塊分配給它.最後再把在堆上分配的字符數組的首地址賦給p.,這個時候,大家已經清楚了,p中現在存放的是在堆中申請的字符數組的首地址,也就是在堆中申請的數組的地址現在被賦給了在棧上申請的指針變量p.爲了更加形象的說明問題,請看下圖:

這裏寫圖片描述

從上圖可以看出,我們在堆上動態分配的數組的首地址存入了指針p所指向的內容.

請 注意:在棧上所申請的內存空間,當我們出了變量所在的作用域後,系統會自動我們回收這些空間,而在堆上申請的空間,當出了相應的作用域以後,我們需要顯式 的調用delete來釋放所申請的內存空間,如果我們不及時得對這些空間進行釋放,那麼內存中的內存碎片就越來越多,從而我們的實際內存空間也就會變的越 來越少,即,孤立的內存塊越來越多.在這裏,我們知道,堆中的內存區域不是連續的,還是將有效的內存區域經過鏈表指針連接起來的,如果我們申請到了某一塊
內存,那麼這一塊內存區將會從連續的(通過鏈表連接起來的)內存塊上斷開,如果我們在使用完後,不及時的對它進行釋放,那麼它就會孤立的開來,由於沒有任 何指針指向它,所以這個區域將成爲內存碎片,所以在使用完動態分配的內存(通過NEW申請)後,一定要顯式的對它進行DELETE刪除.對於這一點,一定 要切記...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章