在 C 語言程式設計中,記憶體管理是核心技能之一。不同於高階語言擁有自動垃圾回收機制,C 語言要求程式設計師手動掌控記憶體的分配與釋放。這篇文章將系統性地介紹 <stdlib.h> 標頭檔中提供的四大動態記憶體管理函式:malloc()、calloc()、realloc() 與 free(),並透過實戰範例展示它們的正確用法與常見陷阱。
| 函式原型 | 功能描述 | 關鍵特性 |
|---|---|---|
void *malloc(size_t size) | 分配指定位元組數的記憶體塊 | 內容未初始化(包含隨機垃圾值) |
void *calloc(size_t num, size_t size) | 分配陣列記憶體(元素數 × 元素大小) | 自動將所有位元歸零初始化 |
void *realloc(void *ptr, size_t new_size) | 調整已分配記憶體塊的大小 | 可擴大或縮小;可能移動記憶體位址 |
void free(void *ptr) | 釋放先前分配的記憶體塊 | 防止記憶體洩漏;釋放後指標變成野指標 |
當陣列大小在編譯時期無法確定時,動態分配成為唯一解法。例如讀取不定長度的使用者輸入:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[100] = "Zara Ali";
char *description = NULL;
/* 分配 200 位元組空間 */
description = (char *)malloc(200 * sizeof(char));
if (description == NULL) {
fprintf(stderr, "錯誤:無法分配所需記憶體\n");
return 1;
}
strcpy(description, "Zara ali a DPS student in class 10th");
printf("Name = %s\n", name);
printf("Description: %s\n", description);
/* 釋放記憶體,良習必養 */
free(description);
description = NULL; // 避免野指標
return 0;
}
關鍵提醒: malloc 回傳 void*,雖然 C 語言允許隱性轉型,但加上 (char *) 強制轉型可提升碼可讀性與 C++ 相容性。永遠檢查回傳值是否為 NULL,這是防禦性程式設計的基本原則。
當需要初始化為零的陣列時,calloc 比 malloc + memset 更簡潔:
/* 分配 200 個 char 並自動歸零 */
description = (char *)calloc(200, sizeof(char));
兩者效能差異極小,calloc 在大塊記憶體分配時可能因零頁最佳化而更快。
當資料量超出預期,realloc 可在保留原有資料的前提下擴充記憶體:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char *description = (char *)malloc(30 * sizeof(char));
if (!description) return 1;
strcpy(description, "Zara ali a DPS student.");
printf("原始: %s\n", description);
/* 擴充至 100 位元組 */
char *temp = (char *)realloc(description, 100 * sizeof(char));
if (temp == NULL) {
free(description); // realloc 失敗時原記憶體仍有效
return 1;
}
description = temp; // 重要:更新指標,realloc 可能返回新位址
strcat(description, " She is in class 10th");
printf("擴充後: %s\n", description);
free(description);
return 0;
}
致命錯誤避坑指南:
ptr = realloc(ptr, ...):若失敗會導致原記憶體位址遺失,造成記憶體洩漏。務必使用暫存變數接收回傳值。雖然作業系統會在程式結束時回收所有記憶體,但長期運行的程式(如伺服器、嵌入式裝置)必須在不需要時立即呼叫 free()。良好的習慣:
malloc/calloc/realloc 對應一個 free。free(ptr); ptr = NULL; 防止野指標二次釋放或誤用。free 會導致未定義行為。| 錯誤類型 | 錯誤程式碼 | 正確做法 |
|---|---|---|
| 未檢查 NULL | p = malloc(100); strcpy(p, "hi"); | 檢查 if (!p) { handle_error(); } |
| 釋放後繼續使用 | free(p); printf("%s", p); | free(p); p = NULL; |
| realloc 直接賦值 | p = realloc(p, 200); | 使用暫存變數 tmp = realloc(...) |
| 釋放非堆疊記憶體 | char *p = "hello"; free(p); | 僅釋放 malloc/calloc/realloc 取得的位址 |
掌握動態記憶體管理是從 C 語言初學者邁向系統級程式設計師的關鍵門檻。記住三大鐵律:檢查回傳值、配對釋放記憶體、釋放後歸零指標。配合 Valgrind、AddressSanitizer 等工具偵測記憶體洩漏與越界存取,能讓你的 C 程式碼更健壯、更專業。