C 語言記憶體管理 (Memory Management)
在 C 語言中,我們對於記憶體有絕對的控制權。這是一把雙面刃:用得好效能飛天,用不好程式崩潰 (Segmentation Fault) 或造成記憶體洩漏 (Memory Leak)。
Stack vs Heap
記憶體主要分為兩區:
- Stack (堆疊):
- 儲存區域變數 (Local Variables)、函式參數。
- 自動管理:函式結束時自動釋放。
- 空間較小,存取速度快。
- 特點:生命週期短,隨函式生滅。
- Heap (堆積):
- 手動管理:程式設計師負責申請 (
malloc) 和釋放 (free)。 - 空間較大,適合儲存動態大小的資料 (例如讀去未知大小的檔案)。
- 特點:生命週期長,直到被
free或程式結束才消失。
- 手動管理:程式設計師負責申請 (
動態記憶體配置函式
這些函式都在 <stdlib.h> 中。
1. malloc (Memory Allocation)
配置指定 byte 大小的記憶體。注意:它不會初始化內容(裡面可能是垃圾值)。
// 配置一個整數的空間
int* ptr = (int*)malloc(sizeof(int));
// 配置 5 個整數的陣列空間
int* arr = (int*)malloc(5 * sizeof(int));
2. calloc (Contiguous Allocation)
配置記憶體,並將內容全部初始化為 0。速度比 malloc 稍慢,但較安全。
// 配置 5 個整數,且保證 arr[0]...arr[4] 都是 0
int* arr = (int*)calloc(5, sizeof(int));
3. realloc (Re-Allocation)
重新調整已配置記憶體的大小(保留原內容)。
ptr = (int*)realloc(ptr, 10 * sizeof(int)); // 擴充成 10 個 int
4. free (釋放記憶體)
非常重要! 用完的記憶體一定要歸還。
free(ptr);
ptr = NULL; // 最佳實踐:釋放後設為 NULL
常見陷阱 (Common Pitfalls)
動態記憶體管理是 Bug 的溫床,以下是最常見的三種錯誤。
1. 記憶體洩漏 (Memory Leak)
使用了 malloc 卻忘記 free,導致這塊記憶體一直被佔用,直到程式結束。如果是伺服器程式,記憶體會慢慢被吃光。
void leak() {
int *p = malloc(100);
// 做事...
// 糟糕!忘記 free(p) 就離開了
}
2. 重複釋放 (Double Free)
對同一個位址 free 兩次,會導致程式崩潰。
free(ptr);
// ... 一堆程式碼 ...
free(ptr); // 錯誤!這裡會 Crash
解法:釋放後立刻將指標設為 NULL。對 NULL 呼叫 free 是安全的(什麼都不會發生)。
free(ptr);
ptr = NULL;
free(ptr); // 安全,因為 ptr 已經是 NULL
3. 使用已釋放的記憶體 (Use After Free)
記憶體還給系統了,卻還試著去讀寫它。這會導致不可預期的行為。
free(ptr);
*ptr = 10; // 錯誤!這塊地已經不是你的了
最佳實踐 (Best Practices)
檢查返回值:
malloc如果記憶體不足會回傳NULL,務必檢查。int *p = malloc(sizeof(int) * 1000); if (p == NULL) { fprintf(stderr, "記憶體配置失敗!\n"); return 1; }誰配置,誰釋放 (Ownership):
- 如果函式內
malloc,盡量在同一個函式內free。 - 如果是工廠模式 (Factory Pattern) 產生物件,要在文件註明「呼叫者負責 free」。
- 如果函式內
配對寫法: 寫下
malloc的當下,馬上在下面寫好free,然後再把邏輯填在中間。
學會自己要記憶體,就要學會自己還。這就是 C 語言的哲學。