1. 程式人生 > >C/C++中棧(stack)與堆(heap)的區別

C/C++中棧(stack)與堆(heap)的區別

棧(stack)
由編譯器自動分配釋放管理。區域性變數及每次函式呼叫時返回地址、以及呼叫者的環境資訊(例如某些機器暫存器)都存放在棧中。新被呼叫的函式在棧上為其自動和臨時變數分配儲存空間。

堆(heap)
需要由程式設計師分配釋放管理,若程式設計師不釋放,程式結束時可能由OS回收。通常在堆中進行動態儲存分配。

全域性區(靜態區)(static)
全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。該部分記憶體在程式退出時由系統進行釋放。

常量區
存放常量字串等常量資訊。該部分記憶體在程式退出時由系統進行釋放。

程式程式碼區
用於存放函式體的二進位制程式碼。

棧和堆的主要區分點

  • 申請方式
    棧(stack)是由系統統一自動分配的。 例如,在函式中聲明瞭一個區域性變數 char a,系統自動會在棧中為a開闢空間。
    堆(heap)則是由程式設計師自行申請,並指明所需的空間大小,然後進行分配的。在C中使用malloc函式(對應free函式進行釋放),在C++中使用new運算子(對應delete運算子進行釋放)。

  • 響應分配方式
    棧(stack):只要棧的剩餘空間大於所申請空間,系統將為程式提供記憶體,否則將報異常提示棧溢位。
    堆(heap):首先作業系統會維護一個記錄空閒記憶體地址的連結串列,每當系統收到程式的堆記憶體分配申請時,先遍歷該連結串列,並尋找出第一個空間大於所申請空間的堆結點,然後將該結點從空閒結點連結串列中刪除,並將該結點的空間分配給程式。對於大多數系統,都會在這塊記憶體空間中的首地址處記錄本次分配的大小,確保程式碼中的delete語句能夠正確的釋放本記憶體空間。由於找到的堆結點的大小不一定正好等於申請的大小,因此係統會自動的將多餘的那部分重新放入空閒連結串列中,用於下一次的分配申請。

  • 可使用的大小限制
    棧(stack):棧是向低地址擴充套件的資料結構(在Windows下),是一塊連續的記憶體的區域。也就是說棧頂的地址和棧的最大容量是系統預先規定好的,因此,能從棧獲得的空間很有限,且很小。如果程式在申請棧空間時,大小超出了當前棧的大小,將觸發溢位錯誤。
    堆(heap):堆是向高地址擴充套件的資料結構,是不連續的記憶體區域。系統採用連結串列來儲存的空閒記憶體地址的,而連結串列的遍歷方向是由低地址向高地址。堆的大小受限於計算機系統中有效的虛擬記憶體。因此,堆可獲得的空間比較靈活,比較大。

  • 堆和棧中的儲存內容
    棧(stack):在函式呼叫時,函式呼叫語句的下一條可執行語句的地址首先進棧,接著是函式的各個引數,一般是由右往左的順序進行入棧操作,再然後是函式中的區域性變數。 當本次函式呼叫結束後,以與入棧相反的順序進行出棧操作,區域性變數先出棧,接著是引數,最後棧頂指標指向最開始存的地址,也就是主函式中的下一條指令,程式以該點位置繼續往後執行。
    堆(heap):一般是在堆的頭部用一個位元組存放堆的大小。堆中的具體內容則有程式設計師安排。

經典程式說明示例

//main.cpp 
int a = 0; // 全域性初始化區 
char *p1;  // 全域性未初始化區 
main() 
{ 
    int b; // 棧 
    char s[] = "abc"; // 棧 
    char *p2; // 棧 
    char *p3 = "123456"; // 123456在常量區,p3在棧上。 
    static int c =0// 全域性(靜態)初始化區 
    p1 = (char *)malloc(10); 
    p2 = (char *)malloc(20); // 分配得來的10和20個位元組的區域在堆區,p1、p2指標變數本身在棧內。 
    strcpy(p1, "123456"); // 123456放在常量區,編譯器可能會將它與p3所指向的"123456"優化成一個地方。 
}