| 特性 | 棧 | 堆 |
|---|---|---|
| 邏輯結構 | 後進先出 (LIFO) 線性表 | 優先隊列,通常用二叉堆(完全二叉樹)實現 |
| 操作端點 | 僅棧頂插入/刪除 | 根節點刪除(取極值),任意位置插入 |
| 典型用途 | 函數調用、遞歸、表達式求值、撤銷操作 | 任務調度、Dijkstra/Prim 算法、Top-K 問題 |
| 存取方式 | 頂部存取,O(1) | 根節點存取 O(1),任意查找 O(n) |
C 程式編譯鏈接後的進程虛擬地址空間佈局(高位→低位):
+------------------+ 高地址 | 內核空間 | +------------------+ | 用戶棧 ↓ | 向下增長,存放局部變數、參數、返回地址 +------------------+ | 共享庫映射 | +------------------+ | 堆 ↑ | 向上增長,malloc/free/new/delete 管理 +------------------+ | BSS 段 | 未初始化全域/靜態變數 +------------------+ | Data 段 | 已初始化全域/靜態變數 +------------------+ | Text 段 | 代碼、只讀數據 +------------------+ 低地址
| 維度 | 棧 | 堆 |
|---|---|---|
| 分配方式 | 編譯器自動分配/釋放 | 程式員手動 malloc/free 或 new/delete |
| 生命週期 | 隨函數進出自動銷毀 | 手動釋放前一直存在,易洩漏 |
| 空間大小 | 有限(Linux 預設 8MB,Windows 1-2MB) | 受限於虛擬記憶體,可達 GB 級 |
| 分配速度 | 極快(指針遞增/遞減) | 較慢(尋找空閒塊、分裂/合併、系統調用) |
| 碎片問題 | 無 | 外部碎片明顯,需記憶體池/智能指針緩解 |
| 存取效率 | 高(CPU 專用指令 push/pop,緩存友好) | 低(間接訪問,可能觸發缺頁中斷) |
| 錯誤類型 | 棧溢出(遞歸過深、大局部數組) | 記憶體洩漏、雙重釋放、使用後釋放 |
int global_init = 10; // Data 段
int global_uninit; // BSS 段
const int const_global = 5; // .rodata (Text 段)
void func(void) {
int local = 20; // 棧
static int static_local = 30; // Data 段
int *p = malloc(100); // p 在棧,指向的 100B 在堆
char str[] = "hello"; // 棧(陣列內容複製自 .rodata)
char *pstr = "world"; // pstr 在棧,"world" 在 .rodata
free(p);
}
先進先出 (FIFO)。常見實現:
front/rear 索引,犧牲一單元區分滿/空。std::unique_ptr/shared_ptr (C++) 或嚴格 malloc/free 配對 (C)。int a[1000000] 直接棧溢出,改用靜態/堆分配。-fstack-protector、AddressSanitizer、Valgrind。棧 = 自助餐:拿盤子(分配)、吃(使用)、走人(自動回收),快但選擇有限。
堆 = 自己做菜:買食材(malloc)、切配炒(管理)、洗碗收拾(free),麻煩但自由度極高。