指標賦予程式任意記憶體讀寫能力,亦帶來記憶體洩漏、野指標、雙重釋放等嚴重風險。本文深入剖析三種記憶體分配方式、malloc/free 與 new/delete 正確用法、記憶體耗盡處理、野指標成因與防禦、指標參數傳遞陷阱,並總結五條鐵律。
| 方式 | 時機 | 生命週期 | 範例 |
|---|---|---|---|
| 靜態儲存區 | 編譯期 | 程式全程 | 全域變數、static 變數 |
| 堆疊 | 函式進入 | 函式返回自動釋放 | 區域變數、函式參數 |
| 堆積 | 執行期 malloc/new | 程式員手動 free/delete | 動態陣列、物件 |
void *malloc(size_t size); // 只管位元組數,回傳 void*
void free(void *memblock); // 僅需指標,自動依原大小釋放
// 正確用法:型別轉換 + sizeof
int *ip = (int*)malloc(sizeof(int) * length);
// 錯誤:sizeof(ip) 只得指標大小,非陣列大小
free(NULL) 安全無副作用;free(同指標兩次) 導致崩潰。// 非 POD 型別必須用 new/delete(自動呼叫建構/解構函式)
class Object {
public: Object() { cout << "Init"; } ~Object() { cout << "Destroy"; }
};
Object *p = new Object; // 分配 + 建構
delete p; // 解構 + 釋放
// 陣列形式
Object *arr = new Object[10];
delete [] arr; // 必須加 []
核心差異:malloc/free 是庫函式,不受編譯器控制,無法自動執行建構/解構;new/delete 是運算子,編譯器保證語意完整。
if (!p) return;if (!p) { cerr << "OOM"; exit(1); }_set_new_handler(MyHandler)(VC++),統一攔截 new 失敗。註:32/64 位元虛擬記憶體機制使實際耗盡極難發生,但錯誤處理仍不可省略。
| 成因 | 範例 | 防禦 |
|---|---|---|
| 未初始化 | int *p; *p = 1; | 宣告即初始化:int *p = nullptr; 或 new int |
| 釋放未歸零 | free(p); *p = 1; | free(p); p = nullptr; |
| 超出作用域 | { int a; p = &a; } *p = 1; | 勿回傳/儲存區域變數位址 |
| 指標運算越界 | p += 100; | 邊界檢查、使用容器 |
| 多執行緒競態 | 一緒釋放、一緒使用 | 智慧指標、鎖、無鎖結構 |
void GetMemory(char *p, int n) { p = (char*)malloc(n); } // 只修改副本
char *str = nullptr; GetMemory(str, 100); // str 仍為 nullptr
void GetMemory(char **p, int n) { *p = (char*)malloc(n); }
GetMemory(&str, 100); // 正確
char *GetMemory(int n) { return (char*)malloc(n); }
str = GetMemory(100); // 簡潔明確
char *GetString() { char s[] = "hello"; return s; } // 編譯警告,執行期亂碼
new ↔ delete、new[] ↔ delete[]、malloc ↔ free。p = nullptr; 斬斷野指標來源。系列導航:(一) 指標概念與四要素 | (二) 記憶體管理與野指標防禦 | (三) 指標運算 | (四) 進階應用與陷阱