| 術語 | 定義 | 別名 |
| 迷途指針 | 指向已釋放/無效內存的指針 | 懸空指針、Dangling Pointer |
| 野指針 | 未初始化的指針 | Uninitialized Pointer |
兩者均指「不指向任何合法對象的指針」,解引用導致未定義行為(UB)。
迷途指針三大成因
1. 釋放內存後指針未置空
char* cp = malloc(100);
free(cp); // 內存歸還系統
// cp 現在是迷途指針,指向已釋放區域
cp = NULL; // 正確做法:釋放後立即置空
2. 返回局部變量地址
int* func() {
int num = 1234;
return # // 錯誤:num 屬於棧幀,函數返回後棧幀銷毀
}
// 調用者拿到的指針指向無效棧內存
解決:用 static、動態分配、或傳出參數。
3. 作用域結束導致
char* cp = NULL;
{
char c = 'x';
cp = &c; // c 屬於內層作用域
} // c 生命週期結束
// cp 成為迷途指針
野指針成因
int f() {
char* cp; // 未初始化,野指針
static char* scp; // 靜態變量自動初始化為 NULL,非野指針
*cp = 'a'; // UB:解引用野指針
}
現代編譯器會警告「使用未初始化變量」。
安全漏洞風險
- 若迷途指針指向的內存被重新分配給敏感數據,寫入操作會破壞數據
- 虛函數調用時 vtable 指針被覆蓋,可導致執行任意代碼(控制流劫持)
- 信息洩露:讀取迷途指針指向的新數據
- 權限提升:若新內存用於安全檢查,可能繞過驗證
防範最佳實踐
1. 智能指針
#include <memory>
std::unique_ptr p(new int(42)); // 自動管理生命週期
std::shared_ptr sp = std::make_shared(42);
2. 釋放後置空
free(ptr); ptr = NULL;
delete p; p = nullptr; // C++11
3. 內存調試工具
- Valgrind:Memcheck 檢測無效讀寫、內存洩漏
- AddressSanitizer (ASan):編譯器插桩,運行時檢測
-fsanitize=address
- Mudflap:GCC 內置指針檢查
- Visual Studio /analyze:靜態分析
4. 內存分配器防護
- DieHard:隨機化分配,虛擬消除迷途指針錯誤
- Boehm GC:保守式垃圾回收,替代 malloc/free
5. 調試器填充模式
釋放後填充特定模式,如 0xDEADBEEF、0xCC、0xCD、0xDD(MSVC),讀取時立即觸發異常。
語言層面的解決
- Java/C#/Go/Rust:無顯式內存釋放,垃圾回收器只在引用計數為 0 時回收,從根源消除迷途指針
- Rust:所有權機制在編譯期防止懸空引用
檢測清單
- 所有指針聲明時初始化(
nullptr 或合法地址)
free/delete 後立即置 nullptr
- 不返回局部變量地址
- 避免多層指針共享同一內存,若需共享用
shared_ptr
- 啟用編譯器警告(
-Wall -Wextra -Wuninitialized)
- CI 集成 ASan/Valgrind 運行時檢測
迷途指針是 C/C++ 最隱蔽、破壞力最強的 Bug 來源之一,嚴格遵循 RAII、使用智能指針、配合工具檢測,可將風險降至最低。
- 上一篇文章: 50 個 C/C++ 源碼學習網站精選大全
- 下一篇文章: C++ Built-in Array 語義深度解析:從 POD 到析構函數的影響
Copyright ©
程式碼之家 All Rights Reserved