1. 程式人生 > >【高質量C++/C總結6】記憶體管理——堆(stack)棧(heap)

【高質量C++/C總結6】記憶體管理——堆(stack)棧(heap)

說在開始:

我提煉了《C++ Primer》、《侯捷C++》、《高質量程式設計指南——C/C++語言》等資料中的重要部分,並總結成此博文。其中涉及到許多我個人對C++的理解,如若有不合理之處,還請朋友們多多指出,我會虛心接受每一個建議。同時,我將實現程式碼放到了我的GitHub上https://github.com/ModestBean/C-Samples,可供下載參考。

記憶體分配方式——堆疊(stack)和棧(heap)

堆疊(stack)

存在於某一個作用域的儲存空間。在函式執行期間,函式內部分區域性變數和形參都會建立在堆疊上。在函式執行結束時,儲存單元會自動在堆疊中清退。由系統分配,分配速度比較快,效率比較高。一般情況不會出現錯誤,但是有時會出現退棧溢位。

堆(heap)

由系統提供的一塊全域性的記憶體空間,程式在執行過程中需要使用malloc或者new申請任意數量的記憶體,可稱為動態記憶體分配,使用非常靈活但是也非常出現問題。

兩者申請方式

void function(){
    Object a;//在堆疊(stack)
    Object *b=new Object();//在堆(Heap)
}

在這段程式碼中應該注意,a是存在堆中的,在函式消亡後就不可在使用,尤其注意不可以使用物件a的引用,因為引用一個不存在的物件是不太現實的。 而物件b在程式的執行過程中都會存在,直到手動delete或者free後才不可以在使用。

兩者申請過程

stack:只要棧還有足夠的剩餘空間,系統都會給程式進行分配,否則分配失敗

heap:應用程式呼叫作業系統OS的記憶體管理模組,搜尋其中是否含有符合要求的空閒連續位元組記憶體塊。在作業系統中,多次進行動態記憶體分配後,會產生許多記憶體碎片,作業系統可能首先需要整理記憶體碎片,然後才能分配成功,在這種情況下就需要很長的時間。

如果對上面的話理解起來有困難,可以參考一下書籍《作業系統》中記憶體管理的章節。

空間大小比較

stack:是一塊記憶體連續區域,也就是說,棧的首地址和大小都是一開始定義好的。如果申請空間大於棧的空間就會出現溢位的現象。預設的堆空間都不大,大概是1M,2M。

heap:在作業系統中,是不連續的記憶體區域,是由作業系統的空閒記憶體連結串列來控制的,(關於空閒記憶體連結串列的問題,可以檢視作業系統的書籍),堆的大小由虛擬記憶體影響,堆的申請空間比較大,但是有時申請速度可能比堆慢。

堆疊中的存取內容

stack:在函式呼叫時,第一個進棧的是主函式中後的下一條指令(函式呼叫語句的下一條可執行語句)的地址,然後是函式的各個引數,在大多數的C編譯器中,引數是由右往左入棧的,然後是函式中的區域性變數。注意靜態變數是不入棧的。當本次函式呼叫結束後,區域性變數先出棧,然後是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式由該點繼續執行。

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

其他堆疊內容

(1)還有另外一種記憶體分配方式:從靜態儲存區分配,這段記憶體在程式編譯的時候就是確定的,在程式的整個執行期間都是存在的,例如全域性變數和static變數等等。

(2)在動態記憶體中,開發人員需要檢查返回值和捕獲異常,來確保記憶體分配是否成功,這需要額外的開銷。千萬不要認為這種這種開銷是可以避免的。千萬不要因小失大。

(3)動態記憶體建立的物件需要手動delete,甚至在刪除後還會繼續使用,或者根本就不回刪除,這時程式就會出現記憶體洩漏等現象。同時這些錯誤也是不容易被發現的。