指標的指標 (`T**`) 是修改呼叫端指標變數本身 (而非指向物件) 的唯一標準方式。野指標則是指向已釋放/未初始化/非法記憶體的指標,是 C/C++ 最隱蔽的錯誤來源之一。
// 場景:函數內部配置記憶體,需讓呼叫端指標指向新記憶體
// ❌ 錯誤:一級指標傳值,修改的是副本
void allocate_wrong(int* p, size_t n) {
p = malloc(n * sizeof(int)); // 修改區域副本 p
// 呼叫端指標未改變 → 記憶體洩漏 + 野指標
}
// ✅ 正確:二級指標傳址,修改呼叫端指標本身
void allocate_correct(int** pp, size_t n) {
*pp = malloc(n * sizeof(int)); // 解參考一次,修改呼叫端指標
}
// 使用
int* ptr = NULL;
allocate_correct(&ptr, 100); // 傳入指標位址
// ptr 現在指向新配置的記憶體
free(ptr);
int _tmain(int argc, _TCHAR* argv[]) {
// 案例 1:一級指標傳值 (修改指向物件,非指標本身)
int* p = malloc(sizeof(int));
flee(p); // 傳入 p 的副本
printf("%d", *p); // 輸出 20 (物件被修改)
free(p);
// 案例 2:二級指標傳址 (修改指標本身)
int* i = malloc(sizeof(int));
flee2(&i); // 傳入 &i (int**)
printf("%d", *i); // 輸出 5
free(i);
}
void flee(int* q) {
*q = 20; // 修改指向的 int 物件
// q 是副本,函數結束銷毀,不影響外部 p
}
void flee2(int** q) {
// 危險模式:指向區域變數
// int local = 5;
// *q = &local; // ❌ 野指標!local 生命週期結束
// 安全模式:修改指向的物件內容
if (q && *q) {
**q = 5; // 修改呼叫端指標指向的 int
}
}
| 成因 | 程式碼模式 | 防範對策 |
|---|---|---|
| 未初始化 | `int* p; *p = 10;` | 宣告即初始化:`int* p = NULL;` 或 `int* p = &var;` |
| 釋放後未置空 | `free(p); *p = 5;` | `free(p); p = NULL;` (雙重 free 安全,解參考 NULL 立即崩潰可被發現) |
| 指向區域變數 | `int* f() { int x; return &x; }` | 回傳動態配置記憶體、靜態變數、或呼叫端提供緩衝區 |
| 記憶體重新配置/移動 | `realloc` 後舊指標失效 | 統一使用 `realloc` 回傳值更新所有別名指標 |
二級指標是 C 語言模擬「引用傳遞」的標準手法,野指標則是手動記憶體管理的代價。理解指標層級、所有權轉移、生命週期綁定,配合現代工具與 C++ RAII 機制,能將這類錯誤降至最低。