在 Visual C++ 6.0 時代至今,理解堆疊與堆積的差異仍是 C/C++ 開發者的必修課。本文系統整理申請方式、系統響應、大小限制、效率比較、儲存內容五大維度,並補充 Windows 虛擬記憶體 VirtualAlloc 進階用法,助你精準選擇分配策略。
| 特性 | 堆疊 | 堆積 |
|---|---|---|
| 分配主體 | 系統自動 (編譯器插入指令) | 程式設計師手動 |
| C 函式 | 無 (宣告區域變數即分配) | malloc/calloc/realloc |
| C++ 運算子 | 無 | new/new[] |
| 指標本身位置 | 堆疊 | 堆疊 (指向堆積記憶體) |
EXCEPTION_STACK_OVERFLOW),通常不可恢復。free/delete 正確釋放。| 項目 | 堆疊 | 堆積 |
|---|---|---|
| 增長方向 | 向低位址 (x86/x64 Windows) | 向高位址 (虛擬位址空間) |
| 連續性 | 連續 | 不連續 (鏈表管理) |
| 預設大小 (Windows) | 1 MB (可透過 /STACK 鏈接器選項調整) | 受限於虛擬記憶體 (32-bit: 2~3 GB; 64-bit: TB 級) |
| 適用場景 | 小型、壽命短、大小固定/可預知 | 大型、動態大小、跨函式生命週期 |
VirtualAlloc/VirtualFree 直接向 OS 申請/釋放頁面對齊的虛擬記憶體區域,繞過 CRT Heap,適合大塊、對齊敏感、需鎖定實體頁面 (如 DMA、巨大陣列) 場景。缺點:粒度 4KB/64KB,管理複雜。高位址
+------------------+
| 呼叫者保存暫存器 |
+------------------+
| 參數 N ... 參數 1 | ← 右至左壓入
+------------------+
| 返回位址 (EIP) | ← CALL 指令壓入
+------------------+
| 呼叫者 EBP | ← prologue: push ebp; mov ebp, esp
+------------------+ ← EBP 指向此處 (幀基址)
| 區域變數 | ← 負位移存取
| ... |
+------------------+
| 暫存器暫存/對齊 |
+------------------+
低位址 (ESP 指向棧頂)
.bss/.data 區,**不在堆疊**。leave (mov esp, ebp; pop ebp) → ret (pop eip),自動彈出區域變數。+------------------+ ← 回傳給使用者的指標
| 使用者資料 |
+------------------+
| 配置器元資料 | ← 大小、旗標、前後鏈指標 (通常位於區塊頭部或腳部)
+------------------+
具體佈局視配置器而定 (Windows Heap、glibc malloc、jemalloc 皆不同)。
std::vector 內部緩衝仍在堆積,但物件本身在堆疊)。unique_ptr, vector, string) 自動管理。reserve、物件池 (Object Pool)、std::pmr::memory_resource (C++17)。new (std::nothrow) 或嘗試捕獲 std::bad_alloc。_CrtDumpMemoryLeaks、AddressSanitizer (Clang/GCC)、Valgrind (Linux)。堆疊與堆積並非對立,而是互補:堆疊負責控制流與暫態資料,堆積負責動態資料與長生命週期物件。現代 C++ 應盡量以標準容器與 RAII 封裝堆積操作,僅在極致效能或特殊對齊需求時直接呼叫 VirtualAlloc 或自訂分配器。理解底層機制,才能寫出高效、穩健的系統級程式碼。