C 記憶體分配方式

2021-08-22 05:54:12 字數 2960 閱讀 5173

摘錄自 《編寫高質量**:改善c++程式的150個建議》

建議27:區分記憶體分配的方式

在c/c++語言中,用記憶體管理的水平去劃分高手與菜鳥已經成為一種不成文的約定:可以從中獲得更好的效能、更大自由的被稱作c++高手,而程式經常面臨著莫名其妙的崩潰,一遍遍的除錯,費時又費力的則可能是菜鳥級別的。而這一切都源於那讓人又愛又恨的c++記憶體管理的靈活性。其中,多樣的記憶體分配方式就是其靈活性的最好例證之一。

乙個程式要執行,就必須先將可執行的程式載入到計算機記憶體裡,程式載入完畢後,就可以形成乙個執行空間,並按照圖3-1所示的那樣進行布局。

**區(code area)存放的是程式的執行**;資料區(data area)存放的是全域性資料、常量、靜態變數等;堆區(heap area)存放的則是動態記憶體,供程式隨機申請使用;而棧區(stack area)則存放著程式中所用到的區域性資料。這些資料可以動態地反應程式中對函式的呼叫狀態,通過其軌跡也可以研究其函式機制。其中,除了**區不是我們能在**中直接控制的,剩餘三塊都是我們編碼過程中可以利用的。在c++中,資料區又被分成自由儲存區、全域性/靜態儲存區和常量儲存區,再加上堆區、棧區,也就是說記憶體被分成了5個區。這5種不同的分割槽各有所長,適用於不同的情況。

棧(stack)區

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

堆(heap)區

堆就是那些由new分配的記憶體塊,其釋放編譯器不會管它,而是由我們的應用程式控制它,一般乙個new就要對應乙個delete。如果程式設計師沒有釋放掉,那麼在程式結束後,作業系統就會自動**。

自由儲存區

自由儲存區是那些由malloc等分配的記憶體塊,它和堆十分相似,不過它是用free來結束自己生命的。

全域性/靜態儲存區

全域性變數和靜態變數被分配到同一塊記憶體中,在以前的c語言中,全域性變數又分為初始化的和未初始化的,在c++裡面沒有作此區分,它們共同占用同一塊記憶體區。

常量儲存區

這是一塊比較特殊的儲存區,裡面存放的是常量,不允許修改。

上述5種分割槽中,最常用的就是堆與棧,容易混淆的也是堆與棧。在bbs論壇裡,幾乎到處都能看到堆與棧的爭論。堆與棧的區分問題,似乎是每乙個c++程式設計師成長路上都會遇到的永恆話題。那麼堆與棧之間到底有什麼分別與聯絡呢?這就是接下來我要闡述的問題。

首先,還是分析下面的**片段:

const int count = 10;  

void function()  

你是否相信就這麼簡單的乙個函式,它卻涉及了5種記憶體分割槽中的3種呢?count是乙個常量,被安置在了常量儲存區,不可修改;pstr是區域性變數,理所應當地放入棧裡;而通過new string[count]獲得的則是一塊堆空間。多麼精妙,多麼不可思議!當然,上述**片段只是乙個示例,是經不起推敲的,因為它會引起記憶體洩露(缺少與new對應的delete去釋放記憶體)。

似乎脫離了主題,還是言歸正傳,說說堆與棧的區別。總的來說,二者的區別主要有以下幾個方面:

管理方式不同

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

空間大小不同

一般來講在32位系統下,堆記憶體可以達到4gb的空間,從這個角度來看堆記憶體幾乎是沒有什麼限制的。但是對於棧來講,一般都是有一定空間大小的。

碎片問題

對於堆來講,頻繁的new/delete勢必會造成記憶體空間的不連續,從而產生大量的碎片,使程式效率降低。對於棧來講,則不存在這個問題,其原因還要從棧的特殊資料結構說起。棧是乙個具有嚴明紀律的佇列,其中的資料必須遵循先進後出的規則,相互之間緊密排列,絕不會留給其他資料可插入之空隙,所以永遠都不可能有乙個記憶體塊從棧中間彈出,它們必須嚴格按照一定的順序一一彈出。

生長方向

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

分配方式

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

分配效率

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

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

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

請記住:

記憶體分配具有多種不同的方式,它們各具特點,適用於不同的情形。所以,要在合適的地方採用合適的方式完成記憶體的分配。

C記憶體分配方式與C 記憶體分配方式

c記憶體分配方式 注 malloc函式的實質體現在,它有乙個將可用的記憶體塊連線為乙個長長的列表的所謂空閒鍊錶。呼叫malloc函式時,它沿連線表尋找乙個大到足以滿足使用者請求所需要的記憶體塊。然後,將該記憶體塊一分為二 一塊的大小與使用者請求的大小相等,另一塊的大小就是剩下的位元組 接下來,將分配...

C 記憶體分配方式

在c 中,記憶體分成5個區 堆 棧 自由儲存區 全域性 靜態儲存區和常量儲存區。棧 就是那些由編譯器在需要的時候分配,在不需要的時候自動清除的變數的儲存區。裡面的變數通常是區域性變數 函式引數等。在乙個程序中,位於使用者虛擬位址空間頂部的是使用者棧,編譯器用它來實現函式的呼叫。和堆一樣,使用者棧在程...

C 記憶體分配方式

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