是,delete后未置nullptr是最常见且易修复的悬空指针来源;C++不自动置空,p仍保留原地址,误用将导致未定义行为,应每次delete后立即赋值p = nullptr。

指针被释放后还在用,delete 后没置 nullptr
这是最常见也最容易修复的悬空指针来源。C++ 不会自动把指针设为 nullptr,delete p 只是归还内存,p 本身仍保留原地址值,后续若误用(比如再次 delete p 或解引用),就触发未定义行为。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 每次
delete后立刻手动赋值p = nullptr,哪怕只是临时变量——这能拦截二次释放和意外解引用(解引用nullptr至少会崩得明确) - 避免裸指针管理生命周期:优先用
std::unique_ptr或std::shared_ptr,它们在析构时自动释放且内部指针不可再访问 - 如果必须用裸指针(如对接 C API),把
delete+= nullptr封装成宏或内联函数,减少遗漏
栈对象地址被保存到堆指针里,&local_var 赋给 new 出来的结构
典型野指针场景:函数返回局部变量的地址,或把局部变量地址存进动态分配的对象中。函数退出后栈帧销毁,该地址立即失效,但指针值还在,变成指向垃圾内存的野指针。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 禁止返回局部变量的地址或引用,编译器通常会警告
returning address of local variable,别忽略它 - 检查所有形如
some_struct->ptr = &local_buffer;的赋值,确认local_buffer的生命周期是否覆盖整个使用期;否则应改为深拷贝或改用堆分配 - 启用编译器检查:
-Wreturn-stack-address(GCC/Clang)能捕获多数此类问题
std::vector 或容器重分配导致迭代器/指针失效
std::vector 在 push_back、resize 等操作中可能重新分配内存,原有元素地址全部作废。此时若还拿着旧指针或迭代器去访问,就是典型的悬空指针,且不报错、行为随机。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 避免长期持有
std::vector元素的裸指针或原始地址(如&v[0]);需要稳定地址时改用std::deque或std::list,或用索引代替指针 - 若必须用指针,每次容器修改后重新获取(如
p = &v[i]),或改用std::vector<std::unique_ptr<T>>,让元素本身在堆上不动 - 注意
std::vector::data()返回的指针在任何可能导致重分配的操作后都失效,不是“只读安全”
多线程环境下指针被另一个线程释放,本线程还在用
单线程下安全的指针,在并发场景下极易变成野指针:线程 A 刚 delete p,线程 B 还在解引用 p,中间没有同步机制,结果完全不可预测。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 裸指针几乎无法安全用于跨线程共享;必须用智能指针配合原子操作,例如
std::shared_ptr的引用计数是线程安全的,多个线程可同时持有并安全析构 - 避免在线程间传递裸指针;若必须传,用
std::atomic<T*>包装,并确保所有访问(包括读、写、解引用)都通过原子 load/store 控制时序 - 更稳妥的做法是设计数据所有权归属单一主线程,其他线程只通过消息队列或回调获取副本,不直接持有原始指针
真正难防的是那些隐式生命周期耦合:比如一个类把成员指针传给了外部回调函数,却没文档说明“你必须在我析构前停用该回调”。这种地方不会报错,也不会崩溃,直到某次内存复用恰好撞上脏数据——这时候连 ASan 都不一定抓得住。


















