野指针是未初始化就使用的指针,指向随机地址,可能引发段错误或静默崩溃;悬空指针是曾合法但所指内存已被释放而指针未置空的指针,解引用会导致不可预测行为。

野指针是“没初始化就用”的指针
野指针指向的内存地址完全随机,可能是非法地址、已映射但不可访问的区域,甚至恰好是某个合法地址——但这纯属巧合,不构成安全依据。它根本没被赋予有效目标,所以连“曾经合法”都不算。
常见错误现象:Segmentation fault (core dumped) 或直接静默崩溃;调试时发现 ptr 值像 0x7fffe8a12345 这种毫无规律的十六进制数;用 valgrind 会报 Use of uninitialised value。
- 典型场景:局部指针变量声明后没赋值就传给函数或解引用,比如
int* p; *p = 42; - 结构体/类中裸指针成员未在构造函数里初始化(尤其默认构造函数)
- C 风格数组指针误写成
int* arr; arr[0] = 1;而非int* arr = new int[10]; - 编译器不会报错,
-Wall也常漏掉——必须依赖-Wuninitialized或静态分析工具
悬空指针是“曾经合法但已被释放”的指针
悬空指针曾指向一块有效的堆内存(或栈内存),但那块内存已被 delete 或 free(),而指针本身没被置为 nullptr。此时解引用不是“随机”,而是读写已归还的内存,后果更隐蔽:可能暂时不出错,也可能覆盖其他变量、触发后续崩溃。
常见错误现象:程序偶尔 crash,且 crash 点和出问题的指针位置不一致;valgrind 报 Invalid read/write of size X 并指出地址曾被 free() 过;多线程下极易引发竞态。
立即学习“C++免费学习笔记(深入)”;
- 典型场景:
int* p = new int(5); delete p; *p = 10;—— 这里p就成了悬空指针 - 返回局部对象地址:
int* get_ptr() { int x = 42; return &x; },调用后指针立即悬空 - 智能指针管理下仍手动
delete对应内存(绕过 RAII),导致其他shared_ptr持有悬空引用 -
std::vector::data()在 vector 重新分配后失效,继续用旧指针就是悬空
怎么快速判断一个指针是野还是悬空?
不能靠打印值猜——野指针可能打印出 0x0(极小概率),悬空指针也可能残留原地址。唯一可靠方式是结合上下文追踪生命周期。
- 看定义:如果指针声明后**没显式赋值**(包括
= nullptr),优先怀疑野指针 - 看操作:如果指针曾成功指向某对象,之后出现
delete/free()/ 函数返回 / 容器重分配等**资源释放动作**,再使用就属悬空 - 用工具:启用
AddressSanitizer(-fsanitize=address),它能区分两类错误——野指针触发use-of-uninitialized-value,悬空指针触发heap-use-after-free或stack-use-after-scope - 加防御:对裸指针,释放后立刻设为
nullptr;但注意这只能防解引用崩溃,不能解决所有逻辑问题
为什么智能指针不能彻底消灭这两类问题?
智能指针管的是“所有权”,不是“所有访问”。它们能极大降低悬空风险,但对野指针无感,且在某些边界下仍会生成悬空语义。
-
std::unique_ptr和std::shared_ptr声明时不初始化,仍是野指针:std::unique_ptr<int> p; *p = 1;</int>直接 UB - 用
get()拿到裸指针后,原智能指针若被销毁,该裸指针立刻悬空 - 循环引用导致
shared_ptr不释放内存,看似没悬空,实则资源泄漏;而强行reset()又可能让其他弱引用变悬空 - 自定义删除器写错(比如删了不该删的内存),会让智能指针变成“主动制造悬空”的工具
实际写代码时,最容易被忽略的是:野指针在调试构建下有时会表现为 0 初始化(尤其全局/静态变量),让人误以为安全;而悬空指针在内存未被复用前行为稳定,测试很难覆盖。这两点共同导致问题只在特定环境、特定负载下爆发。










