什麼是堆?什麼是棧?

2021-05-12 20:11:27 字數 3292 閱讀 9589

一 英文名稱

堆和棧是c/c++程式設計中經常遇到的兩個基本概念。先看一下它們的英文表示:

堆――heap

棧――stack

二 從資料結構和系統兩個層次理解

在具體的c/c++程式設計框架中,這兩個概念並不是並行的。深入到彙編級進行研究就會發現,棧是機器系統提供的資料結構,而堆

是由c/c++函式庫提供的。這兩個概念可以從資料結構和系統兩個層次去理解:

1、從資料結構層次理解,棧是一種先進後出的線性表,只要符合先進後出的原則的線性表都是棧。至於採用的儲存方式(實現方式

)是順序儲存(順序棧)還是鏈式儲存(鏈式棧)是沒有關係的。堆則是二叉樹的一種,有最大堆最小堆,排序演算法中有常用的堆排

序。2、從系統層次理解,棧是系統為執行的程式分配的先進後出的儲存區域。在學習bootloader時知道,在上電後初始化階段要為各個

工作模式下分配堆疊,這裡的堆疊實際上就是指stack,堆疊的說法只是因為歷史的原因。在執行函式時,函式內部區域性變數的儲存

單元可以在棧上建立(針對cisc架構而言,risc架構下,區域性變數的儲存單元是在暫存器上建立),函式執行結束時這些儲存單元自

動被釋放。堆是系統管理的可以被程式利用的全域性儲存空間,動態記憶體分配就是從堆上分配。

具體地說,現在計算機(序列執行機制),都直接在**層次支援棧這種資料結構。這體現在,有專門的暫存器指向棧所在的地

址,有專門的機器指令完成資料入棧出棧的操作。比如arm指令中的stmfd和ldmfd。因為棧記憶體分配運算內置於處理器的指令集中,

所以效率很高,但是支援的資料有限,一般是整數、指標、浮點數等系統直接支援的資料型別,並不直接支援其他的資料結構。在

cisc中,對子程式的呼叫就是利用棧來完成的。c/c++中的自動變數也是直接利用棧的例子,這就是為什麼當函式返回時,該函式的

自動變數失效的原因(因為棧恢復了呼叫前的狀態)。在risc下,這些都是通過暫存器來完成的。這些留待第二部分總結中詳細闡述

和棧不同,堆的資料結構並不是由系統(無論是機器系統還是作業系統)支援的,而是由函式庫提供的。基本的malloc/free函

數維護了一套內部的堆資料結構。當程式使用這些函式去獲得新的記憶體空間時,這套函式首先試圖從內部堆中尋找可用的記憶體空間。

如果沒有可用的記憶體空間,則試圖利用系統呼叫來動態增加程式資料段的記憶體大小,新分配得到的空間首先被組織進內部堆中去,然

後再以適當的形式返回給呼叫者。當程式釋放分配的記憶體空間時,這片記憶體空間被返回記憶體堆結構中,可能會被適當的處理(比如和

其他空閒空間合併成更大的空閒空間),以更適合下一次記憶體分配申請。這套複雜的分配機制實際上相當於乙個記憶體分配的緩衝池(

cache),使用這套機制有如下原因:

1、系統呼叫可能不支援任意大小的記憶體分配。有些系統的系統呼叫只支援固定大小及其倍數的記憶體請求(按頁分配),這樣的

話對於大量的小記憶體分類來說會造成浪費。

2、系統呼叫申請記憶體可能是代價昂貴的,因為這可能涉及到使用者態和核心態的轉換。

3、沒有管理的記憶體分配在大量複雜記憶體的分配釋放操作下很容易造成記憶體碎片。

總結:1、棧是系統提供的功能,特點是快速高效,缺點是有限制,資料不靈活;而堆是函式庫提供的功能,特點是靈活方便,資料適

應面廣泛,但是效率有一定降低。

2、棧是系統資料結構,對於程序/執行緒是唯一的;堆是函式庫內部資料結構,不一定唯一,不同堆分配的記憶體無法互相操作。

3、棧空間分靜態分配和動態分配兩種。靜態分配是編譯器完成的,比如自動變數(auto)的分配。動態分配由alloc函式完成。棧

的動態分配無需釋放(是自動的),也就沒有釋放函式。為可移植的程式起見,棧的動態分配操作是不被鼓勵的!堆空間的分配總是動

態的,雖然程式結束時所有的資料空間都會被釋放回系統,但是精確的申請記憶體/釋放記憶體匹配是良好程式的基本要素。

三 從系統層次再次細化分析

下面從系統的層次對堆和棧再次進行細緻的對比,以更好的理解這兩個概念。

1、申請方式

stack:系統自動分配。

heap:程式設計師自己申請,指明大小。

2、申請後系統響應

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

heap:首先應該知道作業系統有乙個記錄空閒記憶體位址的鍊錶,當系統收到程式的申請時會遍歷該鍊錶,尋找第乙個空間大於所申請

空間的堆結點,然後將該結點從空閒結點鍊錶中刪除,並將該結點的空間分配給程式,另外,對於大多數系統,會在這塊記憶體空間中

的首位址處記錄本次分配的大小,這樣,**中的delete語句才能正確的釋放本記憶體空間。另外,由於找到的堆結點的大小不一定正

好等於申請的大小,系統會自動的將多餘的那部分重新放入空閒鍊錶中。

3、申請大小的限制

stack:在arm中,stack預先設定好。在start.s中,可以有四種不同的棧:滿棧遞增,滿棧遞減,空棧遞增,空棧遞減。常用的是滿

棧遞減,就是向下(低位址)方向生長。如果分析bootloader的stack設定部分,這個很容易理解。

heap:是向高位址擴充套件的資料結構,是不連續的記憶體區域。這是由於系統是用鍊錶來儲存的空閒記憶體位址的,自然是不連續的,而鏈

表的遍歷方向是由低位址向高位址。堆的大小受限於計算機系統中有效的虛擬記憶體。由此可見,堆獲得的空間比較靈活,也比較大。

4、申請效率比較

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

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

引數,在大多數的c編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。當本次函式呼叫結束

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

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

6、訪問效率比較

stack:在執行時刻賦值,訪問效率高。

heap:在編譯時就確定,訪問效率不如stack。

因為棧是由系統分配,更為確切的說,這就與cpu是risc架構還是cisc架構密切相關的。在兩種不同指令集體系結構中,對待內

存分配的處理上是明顯不同的。在risc架構下,原則之一就是盡量少訪問記憶體,而代之以暫存器解決。所以暫存器的數目要明顯多於

cisc架構的暫存器。而堆是由函式庫來提供的,與系統的關係就遠了。當然,這些區分只有在深入到彙編級程式設計時才能體會到,

高階語言設計時,對於這些分配工作是由編譯系統代勞的。由此也可以看出,編譯系統的效率對程式的執行效率有著重要影響。這些

知識點在以後的讀書實踐中要慢慢體會,可能現在的認識有錯誤或者侷限性,等有更深入的認識時,你的水平也就上了乙個台階。現

在以初學者的身份努力思考學習。。。

什麼是堆?什麼是棧?

一 英文名稱 堆和棧是c c 程式設計中經常遇到的兩個基本概念。先看一下它們的英文表示 堆 heap 棧 stack 二 從資料結構和系統兩個層次理解 在具體的c c 程式設計框架中,這兩個概念並不是並行的。深入到彙編級進行研究就會發現,棧是機器系統提供的資料結構,而堆是由c c 函式庫提供的。這兩...

什麼是堆和棧

一 預備知識 程式的記憶體分配 乙個由c c 編譯的程式占用的記憶體分為以下幾個部分 1 棧區 stack 由編譯器自動分配釋放,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。2 堆區 heap 一般由程式設計師分配釋放,若程式設計師不釋放,程式結束時可能由os 注意它與資料結...

什麼是堆和棧

堆 管資料儲存的。引用資料型別的存放,所以堆的空間是比較大的 每個執行緒都有自己的棧,棧中的資料都是以棧幀 stack frame 的格式為基本單位進行儲存的。棧中儲存的就是乙個個棧幀 在這個執行緒上正在執行的每個方法都各自對應乙個棧幀。方法和棧幀一一對應,乙個方法的執行會伴隨這棧幀入棧,乙個方法的...