記憶體管理是 C/C++ 程式設計師的核心基本功,也是面試必考重點。本文系統整理三種分配方式、五大常見錯誤、指標與陣列差異、參數傳遞陷阱、new/delete 與 malloc/free 區別、記憶體耗盡處理等核心知識,並給出可直接落地的防錯清單。
| 方式 | 典型例子 | 生命週期 | 特點 |
|---|---|---|---|
| 靜態儲存區 | 全域變數、static 變數 | 程式啟動~結束 | 編譯期決定位置與大小,自動初始化為 0 |
| 堆疊 | 函數內區域變數 int a; | 函數呼叫~返回 | CPU 指令級支援,極快,空間有限(通常 1~8 MB) |
| 堆積 | malloc/free、new/delete | 程式設計師手動控制 | 靈活、空間大,但易產生碎片、洩漏、野指標 |
malloc/new 可能回傳 nullptr。std::vector、std::array 並啟用邊界檢查(at()),慎用裸陣列。malloc 與 free、new 與 delete 次數必須配對。std::unique_ptr、std::shared_ptr、std::vector 自動管理生命週期。free/delete 僅釋放記憶體,指標值不變。| 項目 | 陣列 | 指標 |
|---|---|---|
| 儲存位置 | 靜態區或堆疊 | 可指向任意區域(堆疊、堆積、靜態區) |
| 名稱含義 | 代表整塊記憶體(非指標) | 儲存位址的變數,可改變指向 |
| sizeof 結果 | 整個陣列大小(含 \0) | 指標變數大小(32 位 4B,64 位 8B) |
| 賦值/比較 | 不可直接 b=a,需 strcpy/strcmp | p=a 僅複製位址,比較亦是比較位址 |
| 字串字面常數 | char a[]="hi" 可修改 | char* p="hi" 指向唯讀區,修改導致崩潰 |
char a[] = "hello"; a[0] = 'H'; // OK
char *p = "world"; p[0] = 'W'; // 編譯通過,執行期崩潰(修改唯讀區)
char b[10]; strcpy(b, a); // 正確複製
// b = a; // 編譯錯誤
char *q = (char*)malloc(strlen(a)+1);
strcpy(q, a); // 正確:先分配再複製
// q = a; // 錯誤:僅複製位址,且記憶體洩漏
void GetMemory(char *p, int n) { p = (char*)malloc(n); } // 形參修改不影響實參
void Test() { char *str = NULL; GetMemory(str, 100); strcpy(str, "hi"); } // 崩潰
void GetMemory(char **p, int n) { *p = (char*)malloc(n); }
void Test() { char *str = NULL; GetMemory(&str, 100); /* 用完 free(str) */ }
char* GetMemory(int n) { return (char*)malloc(n); }
void Test() { char *str = GetMemory(100); /* 用完 free(str) */ }
void GetMemory(char* &p, int n) { p = new char[n]; }
void Test() { char *str = nullptr; GetMemory(str, 100); /* 用完 delete[] str */ }
鐵律:永遠不要返回指向「堆疊記憶體」的指標/參考,函數結束時該記憶體已銷毀。
if (p != nullptr) 無法防禦野指標。free(p); p = nullptr; / delete p; p = nullptr;free/delete 後未置 nullptr。| 維度 | malloc/free | new/delete |
|---|---|---|
| 本質 | C 標準庫函數 | C++ 運算子 |
| 型別安全 | 需手動轉型 (T*) | 自動推導型別,型別安全 |
| 建構/解構 | 不呼叫 | 自動呼叫建構函數/解構函數 |
| 陣列分配 | malloc(n*sizeof(T)) | new T[n] / delete[] |
| 失敗處理 | 回傳 nullptr | 拋出 std::bad_alloc(可用 nothrow 版本) |
| 重新分配 | realloc 可擴縮 | 無直接對應,需手動分配新舊複製 |
原則:C++ 類別物件必須用new/delete;內建型別兩者皆可,推薦new/delete保持風格一致。嚴禁混用(free(new T)漏呼解構函數,delete malloc(...)行為未定義)。
exit(1) 終止程式:簡潔,避免錯誤擴散,適合多分配點的關鍵路徑。std::set_new_handler 註冊回調(記錄日誌、釋放快取、嘗試回收)。現代 64 位系統虛擬記憶體極大,bad_alloc 極難觸發,但錯誤處理不可省略,體現程式碼品質。
void* malloc(size_t size):僅關心位元組數,不識別型別。sizeof(T) * count 計算大小,避免硬編碼位元組數。free(nullptr) 安全無效果;free 同一指標兩次 → 未定義行為(通常崩潰)。int* p = new int[100]; delete[] p; —— 陣列必須配對 [],否則僅釋放首元素。new T、new T(arg)、new T{arg}(C++11 列表初始化)。new T[100] 不能帶參數。new (buffer) T(args) 在預先分配的緩衝區建構物件,常用於記憶體池、嵌入式開發。std::unique_ptr/shared_ptr/vector/string,讓編譯器幫你管記憶體,專注業務邏輯。參考資源:《C++ 物件模型》、《Effective Modern C++》、ISO C++ Core Guidelines (github.com/isocpp/CppCoreGuidelines)