「堆與棧的區別」是 C/C++ 面試與日常開發中永恆的經典話題。本文從內存佈局、分配機制、效率特性、存儲內容、訪問速度五個維度,結合代碼示例與匯編對比,徹底釐清兩者的本質差異。
一個典型的 C/C++ 編譯程序在運行時佔用的虛擬地址空間主要分為五大區域:
malloc/free 或 new/delete),若不釋放由 OS 回收,分配方式類似鏈表。int a = 0; // 全局初始化區
char *p1; // 全局未初始化區
int main() {
int b; // 棧
char s[] = "abc"; // 棧 (數組內容複製自常量區)
char *p2; // 棧
char *p3 = "123456"; // p3 在棧,"123456\0" 在常量區
static int c = 0; // 全局/靜態初始化區
p1 = (char *)malloc(10); // 堆分配 10 字節
p2 = (char *)malloc(20); // 堆分配 20 字節
strcpy(p1, "123456"); // "123456\0" 在常量區,編譯器可能優化合併
}
| 特性 | 棧 | 堆 |
|---|---|---|
| 分配主體 | 系統自動 | 程序員手動 (malloc/new) |
| 指針變量本身 | 存放在棧 | 存放在棧 |
| 大小確定 | 編譯期/運行期自動計算 | 運行期指定 |
malloc 遍歷尋找首個滿足大小的塊(首次適配),分割多餘部分回鏈表;塊頭記錄大小供 free 使用。存在內存碎片,分配速度較慢。char s1[] = "aaaaaaaaaaaaaaa"; // 棧數組,運行時賦值
char *s2 = "bbbbbbbbbbbbbbbbb"; // s2 在棧,指向常量區/堆
訪問對比(MSVC 編譯匯編):
; a = s1[1] 直接寻址
mov cl, byte ptr [ebp-0Fh]
mov byte ptr [ebp-4], cl
; a = s2[1] 間接寻址
mov edx, dword ptr [ebp-14h] ; 取指针值
mov al, byte ptr [edx+1] ; 解引用
mov byte ptr [ebp-4], al
棧上數組直接偏移訪問,指針需先取值再解引用,棧訪問更快。
用棧像去餐廳吃飯:點菜(申請)、付錢、吃(使用)、走人。快捷省心,但自由度低(菜單固定、份量固定)。
用堆像自己做菜:買菜、洗菜、切菜、炒菜、洗碗。麻煩耗時,但自由度極高(想吃啥做啥、份量隨意)。