指針四大核心屬性
- 指針類型:決定解引用訪問大小、指針算術步長(`int*` +1 跳 4 字節)。
- 指針指向類型:即指針指向的對象類型(`int*` 指向 `int`)。
- 指針佔用內存:32 位系統 4 字節,64 位 8 字節(`sizeof(void*)`)。
- 指針值:存儲的地址數值,或 NULL (0)。
野指針三大來源
- **未初始化**:局部指針變量默認隨機值(如 `0xCCCCCCCC`)。
- **釋放後未置空**:`free(p); p = NULL;` 缺失,導致 `p` 指向已釋放內存。
- **變量生命周期結束**:返回局部變量地址、指向已銷毀對象(如類內引用成員綁定臨時對象)。
動態內存管理:malloc/free vs new/delete
- `malloc`/`free`:C 標準庫,僅分配/釋放原始內存,**不調用構造/析構函數**。
- `new`/`delete`:C++ 運算符,分配內存**並調用構造函數**,釋放前**調用析構函數**。
- **嚴禁混用**:`malloc` 分配必須 `free`,`new` 必須 `delete`。對象類型必須用 `new`/`delete`。
- 分配失敗:`malloc` 返回 NULL,`new` 拋出 `bad_alloc` (或 `nothrow` 版返回 NULL)。
指針參數傳遞陷阱
函數參數為值傳遞。若要修改實參指針指向,需用:
- 二級指針 `void f(T **p)`
- 引用 `void f(T *&p)` (C++ 推薦)
- 返回值 `T* f()`
經典錯誤代碼集錦
// 1. 返回局部數組指針
char* getStr() { char p[]="hello"; return p; } // 錯:棧內存失效
// 2. 參數傳指針無法修改實參
void GetMem(char *p) { p = new char[20]; }
char *str = NULL; GetMem(str); // str 仍為 NULL
// 3. 釋放後繼續使用
free(p); strcpy(p, "oops"); // 野指針
// 4. 刪除數組用 delete 而非 delete[]
int *arr = new int[10]; delete arr; // 未定義行為,應 delete[]
// 5. const 指針 vs 指針常量
const int *p1; // 指向常量的指針,不可 *p1=...
int *const p2 = &x; // 常量指針,不可 p2=...
工程最佳實踐
- **定義即初始化**:`int *p = nullptr;`
- **釋放即置空**:`delete p; p = nullptr;`
- **使用智能指針**:`std::unique_ptr`、`std::shared_ptr` 管理生命周期,避免手動 `delete`。
- **避免裸指針傳遞所有權**:接口設計用智能指針或引用/值語義。
- **開啟編譯器警告**:`-Wall -Wextra -Werror` 捕獲未初始化、返回局部地址等問題。
總結
指針是 C/C++ 靈活性與危險的共同來源。掌握「類型、指向、大小、值」四屬性,理解內存分配/釋放與構造/析構綁定,識別野指針三大來源,避開參數傳遞陷阱,並在現代 C++ 中優先使用智能指針,方能駕馭指針威力。