指針是 C/C++ 核心能力,也是 Bug 溫床:懸空指針、野指針、內存洩漏、緩衝區溢出、const 破壞。本文從**參數傳遞、作用域生命週期、內存邊界、特殊指針 (引用/智能指針)** 四個維度,結合反彙編視角與現代 C++ 最佳實踐,構建指針安全開發檢查清單。
| 參數形式 | 調用端 | 被調用端可否修改實參對象? | 被調用端可否修改實參指針? | 典型場景 |
|---|---|---|---|---|
| `T* p` (指針傳值) | `func(ptr)` | **是** (`*p = ...`) | **否** (修改形參 `p` 不影響實參) | 輸出參數、可選參數 (nullptr) |
| `T*& p` (指針引用) | `func(ptr)` | **是** | **是** (`p = new T` 實參指向新內存) | 工廠函數分配內存、鏈表頭插法 |
| `const T* p` | `func(ptr)` | **否** (編譯器阻擋) | 否 | 只讀訪問、大對象避免拷貝 |
void byPtr(int* p) { *p = 20; p = nullptr; } // 修改值成功,修改指針無效
void byRefPtr(int*& p) { *p = 30; p = nullptr; } // 修改值成功,修改指針**實參也變 nullptr**
void byConstPtr(const int* p) { /* *p = 40; */ } // 編譯錯誤
int main() {
int a = 10; int* pa = &a;
byPtr(pa); // a=20, pa 仍指向 a
byRefPtr(pa); // a=30, pa 變 nullptr (危險!)
}
int* bad() {
int local = 42;
return &local; // 警告:返回棧地址
}
// 調用者拿到野指針,後續棧幀復用導致數據錯亂
bad():
push ebp; mov ebp, esp
sub esp, 4 ; 分配局部變量棧空間
mov [ebp-4], 42
lea eax, [ebp-4] ; 返回局部變量地址 (ebp-4)
leave; ret ; 棧幀銷毀,該地址失效
char* p; std::cin >> p; // 野指針寫入,極大概率崩潰
// 正確:
char* p = new char[100]; std::cin >> p; delete[] p;
// 或更佳:
std::string s; std::cin >> s; // 自動管理
| 類型 | 所有權 | 拷貝語義 | 適用場景 |
|---|---|---|---|
| `std::unique_ptr | 獨占 | 不可拷貝,可移動 | 單一擁有者、RAII、工廠返回、Pimpl |
| `std::shared_ptr | 共享 | 引用計數拷貝 | 多擁有者、緩存、觀察者模式 |
| `std::weak_ptr | 弱引用 | 不增加計數 | 打破循環引用、緩存失效檢查 |
指針賦予 C++ 對內存的極致控制力,但「大權在手,責任在肩」。現代 C++ (C++11/14/17/20) 通過 **智能指針、引用、值語義、容器、算法、RAII** 構建了完整的**無裸指針編程生態**。新代碼**默認不寫裸指針**,僅在與 C 庫交互、極致性能熱路徑、底層數據結構實現時謹慎使用,並封裝於安全接口內。內化避坑清單,讓指針成為利器而非陷阱。