理解堆與棧的區別,是掌握 C/C++ 內存管理、避免內存洩漏、棧溢出、野指針的基石。本文從程式內存分區、申請釋放機制、大小限制、分配效率、存取速度、存儲內容六大維度系統對比,附經典「飯館 vs 自炊」比喻。
// 例子程式
int a = 0; // 全局初始化區
char *p1; // 全局未初始化區 (BSS)
main() {
int b; // 棧
char s[] = "abc";// 棧
char *p2; // 棧
char *p3 = "123456"; // "123456\0" 在常量區,p3 在棧
static int c = 0;// 全局/靜態初始化區
p1 = malloc(10); // 堆
p2 = malloc(20); // 堆
strcpy(p1, "123456"); // 常量區字面值可能與 p3 共享
}
| 維度 | 棧 | 堆 |
|---|---|---|
| 申請方式 | 系統自動(聲明局部變量) | 程式員手動(malloc/new) |
| 釋放方式 | 自動(出作用域) | 手動(free/delete) |
| 大小限制 | Windows 默認 1-2MB,連續空間,向低地址增長 | 受限於虛擬內存,非連續,向高地址增長 |
| 分配效率 | 極快(指針遞增/遞減) | 較慢(鏈表遍歷、分裂合併、碎片整理) |
| 存取速度 | 快(直接寄存器/緩存友好) | 慢(間接訪問,雙重解引用) |
| 存儲內容 | 局部變量、函數參數、返回地址、棧幀 | 動態分配對象,程式員管理 |
char a = 1;
char c[] = "1234567890"; // 棧上數組
char *p = "1234567890"; // 指向常量區/堆
a = c[1]; // 反彙編:mov cl, byte ptr [ebp-0Fh] 直接寄存器間接
a = p[1]; // 反彙編:mov edx, dword ptr [ebp-14h] / mov al, byte ptr [edx+1] 雙重間接
棧上數組直接基於 ebp 偏移訪問,指針需先載入指針值再間接訪問,多一條指令。
棧 = 去飯館吃飯:點菜(申請)、付錢、吃(使用)、走人,無需操心切菜洗碗(管理),快捷但自由度低(菜單固定)。
堆 = 自己動手做菜:買菜、切菜、炒菜、洗碗(全過程自管),麻煩但自由度高(想吃啥做啥),靈活。
malloc/free、new/delete、智能指針)。VirtualAlloc 直接保留進程地址空間,繞過堆管理器,極快且靈活。內存管理無小事,堆棧分明,方能寫出健壯高效的系統級代碼。