堆、棧再了解

今天看到了這個才發現我對堆&棧的印象是有一些偏差的:
int *p=new int[5]
這句代碼里面,p是在棧里面,而數組是在堆里面。
而我以為棧里面只放調用的函數。。naive了

(下面的內容來源于網絡。。如果有不正確的可以討論)

首先聲明&定義&棧:

C語言中:如extern User user;extern int a;就是聲明,變量的聲明就是僅僅告訴編譯器,聲明的變量的存在,要預留一點空間,但并不為其分配內存.定義就是聲明這個變量并真正在內存(堆或棧中)為此變量分配空間.
從編譯原理上來說,聲明就是僅僅告訴編譯器,有個某類型的變量會被使用,但是編譯器并不會為它分配任何內存.而定義就是不僅知道某類型的變量會被使用,并且已經為其分配了內存.
因為 在編譯的時候,編譯器先處理一些特殊數據(宏定義,函數的聲明,變量的聲明),在這個過程中,編譯器 通過聲明 可以預測整個定義需要的內存大小,并且 把這些大小預留起來,留給定義的時候使用(因為雖然定義,但是在 main函數里面沒有調用,一樣的不分配內存)。 如果不事先聲明,直接定義,有可能會造成系統崩潰,出現內存不足,不能分配。 然而現在內存都是1G左右了,所以一般空間都足夠,所以你也可以不聲明直接定義函數。 但是一旦內存緊張,就會出錯,而且程序簡潔性就很低。(不看到定義,還不知道原來還定義了這么一個函數?。。。?/p>

在C++中,如short a;int b;這些是定義,也就是說在上分配了內存;
而對于對象,比如自定義的類:User user;也就是說在棧上分配了引用或句柄,沒有具體分配對象的內存;
User *puser就需要puser=new User();才會真正為對象在堆上分配空間.
在C和C++中,extern User user就是聲明,并沒有分配內存空間.

內存中的各個區塊的作用:

  • 棧(stack):由編譯器自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧。
  • 堆(heap) :一般由程序員分配釋放, 若程序員不釋放,程序結束時可能由OS回收 。注意它與數據結構中的堆是兩回事,分配方式倒是類似于鏈表。malloc和new等操作實際上就是在堆中申請內存,對象使用完后要手動釋放,否則只能等待程序結束時由系統回收,會產生內存泄漏。
  • 全局區(靜態區)(static):全局變量和靜態變量是存儲在一起的,初始化過的全局變量和靜態變量在同一塊區域,未初始化的全局變量和靜態變量存放在一塊相鄰的區域內。此區域由系統在程序結束后釋放。
  • 文字常量區:常量字符串存放于此,在程序結束后由系統釋放。字符常量就是像這樣的 char* str=”abc”;其中的”abc”。在實際情況中,是會復用的,比如變量a和b都賦值為”abc”則實際上他們指向同一塊地址。

然后堆/棧的區別:

  • 棧是機器系統提供的數據結構,計算機會在底層對棧提供支持
    分配專門的寄存器存放棧的地址,壓棧出棧都有專門的指令執行,這就決定了棧的效率比較高。
  • 堆則是C/C++函數庫提供的,它的機制是很復雜的,例如為了分配一塊內存,庫函數會按照一定的算法(具體的算法可以參考 數據結構/操作系統)在堆內存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間(可能是由于內存碎片太多),就有可能調用系統功能去增加程序數據段的 內存空間,這樣就有機會分到足夠大小的內存,然后進行返回。顯然,堆的效率比棧要低得多。

生長方向:

  • 對于堆來講,生長方向是向上的,也就是向著內存地址增加的方向;
  • 對于棧來講,它的生長方向是向下的,是向著內存地址減小的方向增長。

堆和棧相比,由于大量new/delete的使用,容易造成大量的內存碎片;
由于沒有專門的系統支持,效率很低;
由于可能引發用戶態和核心態的切換,內存的申請,代價變得更加昂貴。所以棧在程序中是應用最廣泛的,就算是函數的調用也利用棧去完成,函數調用過程中的參數,返回地址,EBP和局部變量都采用棧的方式存放。所以,我們推薦大家盡量用棧,而不是用堆。雖然棧有如此眾多的好處,但是由于和堆相比不是那么靈活,有時候分配大量的內存空間,還是用堆好一些。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容