为什么C++中访问已delete的悬空指针不会导致程序崩溃?
问题解答
示例代码
#include <iostream> #include<list> using namespace std; template <class T> class Ptr { public: Ptr() { a = nullptr; l.push_back(0); } std::list<int> l; void print_this() { cout<<this<<endl; } protected: int *a; }; int main() { Ptr<int> *ptr = new Ptr<int>(); delete ptr; //ptr = nullptr; //取消该行注释程序会崩溃 auto p = &(ptr->l); cout<<"p is "<<p<<endl; ptr->print_this(); ptr->l.push_back(1); cout<<"size is "<<ptr->l.size()<<endl; cout<<"end"; return 0; }
现象原因
你观察到的是C++标准明确定义的未定义行为(Undefined Behavior, UB),当前运行结果完全是巧合,不代表代码逻辑合法。具体原因如下:
delete操作仅会告知堆内存分配器对应地址的内存已不再使用,分配器可将其回收供后续new操作复用。该操作不会主动清空内存内容,也不会立刻向操作系统申请将该地址段标记为不可访问。只要这块内存未被其他操作覆盖、也没有被操作系统回收,对它的读写暂时不会触发硬件异常。- 未将ptr设为
nullptr时,ptr仍保存原来的对象地址,虽然对象已被销毁,但对应地址的内存数据暂时未被改动,类内list的内部控制结构还保持完好:- 非虚成员函数
print_this的调用不需要访问对象内部数据,仅把ptr的值作为this参数传入函数打印,不会触发错误 - 访问
ptr->l、调用push_back时,用到的list内部头指针、长度等数据还未被破坏,push_back会新申请堆内存存储节点,不会触发异常,所以运行后会输出size为2的结果
- 非虚成员函数
- 将ptr设为
nullptr后,访问ptr->l相当于访问0地址偏移的内存,属于操作系统明确禁止的非法访问行为,会立刻触发段错误导致程序崩溃。
注意:这种"正常运行"的状态完全不可靠,只要在
delete之后增加任意堆内存申请操作,或者更换运行环境、编译参数,都可能直接导致程序崩溃、输出乱码等不可预期的结果,悬空指针访问的代码禁止在生产环境使用。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

