这是练习中的代码:
#include <iostream>
using namespace std;
int main() {
int n = 13;
int* ip = new int(n + 3);
int* ip2 = ip;
cout << *ip << endl;
delete ip;
cout << *ip2 << endl;
cout << ip << tab << ip2 << endl;
}
当删除分配给堆上的int的空间时,我认为取消引用指针会产生某种内存错误。相反,它返回0.
为什么会这样?
答案 0 :(得分:14)
取消引用无效指针会导致每个规范的结果不确定。它不能保证失败。
通常(CPU / OS /编译器/ ...依赖),编译器根本不关心它。它只是给出了当前内存地址的内容。例如,在x86体系结构中,只有当地址位于未映射到进程的内存页面中时(或者您的进程没有访问权限的权限)才会看到错误,因此CPU将抛出异常(保护故障)操作系统将适当处理(并可能使您的过程失败)。有时使用技巧来访问地址0
总是导致访问冲突:操作系统将页表中地址空间第一页的读/写位设置为0,以便对该页进行任何访问总会产生异常。
答案 1 :(得分:4)
此行为未定义,因此将会发生与实现和系统相关的事情。
答案 2 :(得分:3)
取消引用ip
指针将返回当时在该内存位置发生的情况。
它返回0,因为这是ip
处的四个字节在转换为整数时恰好编码的内容。
删除指针后取消引用是不可预测的。它可能为零,如果该内存已在其他地方重新分配,则可能是其他内容。
当你运行你的程序时,你很幸运它是0。
答案 3 :(得分:0)
继Mehrdads回答之后,在带有glibc的gcc中,表示内存堆的数据结构存储在返回的内存块中,以节省内存(即它的侵入列表)。因此,当释放一块内存时,它将被添加到空闲列表中。我的猜测是在free之后写入0表示这是空闲块列表的最后一个元素(释放内存的第一个指针大小的单词将包含列表next
指针)。
如果要在再次取消引用此块之前分配并释放更多内存,则在将新项添加到空闲列表的末尾时,该值将更改。这是库实现决策影响“未定义”行为期间发生的事情的一种方式。在这种情况下,glibc开发人员利用了这一行为未定义的事实,以使其内存分配器更节省空间。
如果您在valgrind下运行程序,它将为您捕获这些错误。在任何情况下,始终远离未定义的行为,因为它很可能在各种平台上不同,甚至在同一平台上的不同构建(例如,调试与发布)。