| 維度 | 數據結構 | 內存分配區域 |
|---|---|---|
| 棧 | 後進先出 (LIFO) 線性表 | 函數調用幀、局部變量、參數、返回地址 |
| 堆 | 優先隊列/完全二叉樹 | 動態分配內存區域 (`malloc`/`new`) |
本文聚焦內存分配區域的堆與棧。
高地址 +------------------+ | 內核空間 (1GB) | +------------------+ | 用戶棧 ↓ 增長 | ← 棧區:自動管理,向下增長 | ... | | 共享庫映射區 | | ... | | 堆 ↑ 增長 | ← 堆區:手動管理,向上增長 | ... | | BSS 段 | | Data 段 | | 代碼段 (Text) | +------------------+ 低地址 (0x08048000 附近)
| 特性 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 編譯器自動分配/釋放 | 程序員手動 `malloc`/`free`、`new`/`delete` |
| 管理效率 | 極高(指針移動) | 較低(鏈表遍歷、分裂/合併、鎖競爭) |
| 空間大小 | 有限(默認 8MB,`ulimit -s` 可調) | 巨大(受限於虛擬地址空間,32位 ~3GB) |
| 碎片問題 | 無(連續) | 有(外部碎片),需內存池/分配器優化 |
| 生存週期 | 函數調用期間 | 手動釋放前一直存在 |
| 訪問速度 | 快(局部性好,緩存友好) | 較慢(間接訪問,可能缺頁) |
| 典型用途 | 局部變量、函數參數、返回地址 | 大對象、動態數組、生命週期不確定對象 |
| 錯誤類型 | 棧溢位、野指針(釋放後訪問) | 內存洩漏、雙重釋放、越界、碎片耗盡 |
int a = 0; // 全局初始化區 (Data)
char *p1; // 全局未初始化區 (BSS)
int main() {
int b; // 棧
char s[] = "abc";// 棟 (數組內容在棧)
char *p2; // 棧 (指針變量)
char *p3 = "123456"; // p3 在棧,"123456\0" 在只讀區
static int c = 0; // 全局/靜態區 (Data)
p1 = malloc(10); // 堆 (10 字節)
p2 = malloc(20); // 堆 (20 字節)
// ...
}
// 棧數組訪問 char a[10]; a[1] = 1; // 1 条指令:mov byte [rbp-10], 1 // 堆指針訪問 char *p = malloc(10); p[1] = 1; // 3 条指令:load ptr, add offset, store
棟訪問無間接引用,指令更少、流水線更友好、緩存命中率更高。
| 陷阱 | 代碼示例 | 後果 | 修正 |
|---|---|---|---|
| 返回棧地址 | `char *f() { char s[10]; return s; }` | 野指針,未定義行為 | 用 `static`、動態分配、或輸出參數 |
| 棧上大數組 | `void f() { int a[1000000]; }` | 棧溢位 | 改用 `malloc`/`vector`/靜態數組 |
| 忘記 free | `p = malloc(100); // 无 free` | 內存洩漏 | RAII/智能指針/嚴格配對 |
| 雙重 free | `free(p); free(p);` | 堆損壞、崩潰 | `free` 後置 `NULL` |
| 釋放後訪問 | `free(p); p[0] = 1;` | Use-after-free,安全漏洞 | 釋放後置 `NULL`,靜態分析/ASan 檢測 |
| 棧指針逃逸 | `int *p = &local; thread_start(p);` | 線程訪問已銷毀棧幀 | 堆分配或同步機制保證生命週期 |
需要動態大小 / 生命週期超出函數 / 大於 ~1KB ?
├─ 是 → 堆 (malloc/free, new/delete, vector, unique_ptr)
└─ 否 → 棧 (局部變量, 數組, 結構體)
└─ 遞歸深度大 / 大對象 → 仍考慮堆
理解堆棧差異,是寫出高效、安全、無洩漏 C/C++ 代碼的基石。