進程虛擬記憶體佈局(32 位元)
0xFFFFFFFF ┌─────────────────────┐
│ 核心空間 (2GB) │ 0x80000000 - 0xFFFFFFFF
│ (共享映射、驅動) │
0x80000000 ├─────────────────────┤
│ 用戶空間 (2GB) │ 0x00000000 - 0x7FFFFFFF
│ ┌───────────────┐ │
│ │ 基棧 │ │ 靜態儲存區:程式碼段、全域/靜態變數、常量
│ │ (靜態儲存區) │ │
│ ├───────────────┤ │
│ │ 堆 │ │ 動態分配:malloc/new,向上增長
│ │ (向上增長) │ │
│ ├───────────────┤ │
│ │ ... │ │
│ ├───────────────┤ │
│ │ 浮動棧 │ │ 函式呼叫幀、區域變數,向下增長
│ │ (向下增長) │ │
0x00000000 └─────────────────────┘
三大記憶體區域詳解
| 區域 | 別名 | 內容 | 生命週期 | 管理方式 |
| 基棧 | 靜態儲存區 | 程式碼段、全域變數、static 變數、const 常量 | 程式啟動~結束 | 編譯器靜態分配 |
| 堆 | 動態儲存區 | malloc/free、new/delete 分配的記憶體 | 手動控制 | 程式員手動管理 |
| 浮動棧 | 棧 | 函式參數、區域變數、返回位址、暫存器保存 | 函式進入~返回 | 編譯器自動管理 (幀指鏈) |
虛擬記憶體機制
- 分頁:4KB 頁框,MMU 將虛擬頁映射到物理頁框。
- 換頁:閒置頁面換出至交換分區/檔案,釋放物理記憶體。
- 缺頁中斷:存取未駐留頁面觸發,OS 從磁碟讀入並更新頁表。
- 写時複製 (COW):fork() 時父子共享頁面,寫入時才複製。
經典記憶體 Bug 與對策
| Bug 類型 | 成因 | 對策 |
| 野指標/懸空指標 | free 後繼續使用、函式返回區域變數位址 | free 後置 NULL、嚴禁返回區域變數指標 |
| 緩衝區溢位 | 陣列越界寫入、strcpy/strcat 無長度檢查 | 用 strncpy/snprintf、界檢查、編譯器強化 (-fstack-protector) |
| 記憶體洩漏 | malloc 無對應 free、例外路徑漏釋放 | RAII (C++)、智慧指標、靜態分析工具 |
| 雙重釋放 | 同一指標 free 兩次 | free 後置 NULL、所有權單一化 |
| 釋放非堆記憶體 | free 靜態/棧記憶體 | 只釋放 malloc/new 分配的記憶體 |
| 迭代器失效 | 釋放鏈表節點後繼續用 next | 先存 next 再 free 當前節點 |
執行緒參數傳遞陷阱
// 錯誤:傳遞區域變數位址
void start_thread_bad() {
int arg = 42;
pthread_create(&tid, NULL, thread_func, &arg); // arg 在棧上,函數返回即失效
}
// 正確:堆上分配參數結構體
typedef struct { int sock; } ThreadArg;
void start_thread_good(int sock) {
ThreadArg *arg = malloc(sizeof(ThreadArg));
arg->sock = sock;
pthread_create(&tid, NULL, thread_func, arg);
}
void *thread_func(void *p) {
ThreadArg *arg = p;
// 使用 arg->sock
free(arg); // 執行緒結束前釋放
return NULL;
}
最佳實踐清單
- 指標初始化為 NULL,使用前檢查。
- 配對分配/釋放:每個 malloc 對應一個 free,new 對應 delete/delete[]。
- 所有權語義明確:誰分配誰釋放,或用智慧指標 (C++11
unique_ptr/shared_ptr)。
- 邊界檢查:字串操作用
snprintf、strncpy,陣列存取加斷言。
- 工具輔助:AddressSanitizer (
-fsanitize=address)、Valgrind、Clang Static Analyzer、Cppcheck。
- 避免手動管理複雜資料結構:優先用標準庫容器 (vector, string, list) 或成熟庫。
64 位元擴展
- 用戶空間擴展至 128TB (x86-64 典型 47 位元),核心空間同樣巨大。
- 指標 8 位元組,
size_t/ptrdiff_t 為 64 位元。
- 棧預設 8MB (Linux),可調整 (
ulimit -s、pthread_attr_setstacksize)。