为何访问已超出作用域的对象仍能得到「正确」输出?
问题说明
以下代码可能会打印10,但assign函数中创建的Test1对象t1在执行打印操作时已经被销毁,那么Test2对象t2的ptr指针是否指向仍保留值10的未分配内存空间?
#include <iostream> class Test1 { public: int a; }; class Test2 { public: Test1* ptr; }; void assign(Test2& t2) { Test1 t1{10}; t2.ptr = &t1; } int main() { Test2 t2; assign(t2); std::cout << t2.ptr->a << std::endl; return 0; }
解答
这属于C++中的未定义行为,没有任何规则能保证你读到的一定是10,也不能认为这块内存还保留着原来的值:
t1是assign函数的局部变量,存储在栈内存中。当函数执行完毕返回时,对应的栈帧会被销毁,t1占用的内存空间会被标记为可被后续代码复用,但不会立刻被清空。- 你偶尔能读到10,只是因为这块内存还没被其他操作覆盖,这是完全随机的巧合。如果在
assign返回后、打印前插入其他函数调用或者变量操作,这块内存很可能被新数据覆盖,此时再访问ptr就会得到错误的值,甚至直接导致程序崩溃。 - 此时
t2.ptr是悬垂指针,指向已经失效的内存区域。C++标准明确规定,通过悬垂指针访问数据的行为是未定义的,程序的任何表现都是合法的,不能依赖这种偶然的“正常输出”。
内容的提问来源于stack exchange,提问作者Rodrigo Pontes
相关产品推荐
相关产品推荐

