C++指针数组在调用delete[]后仍然可访问

3
在下面的代码中,调用了 delete[] 一次以释放由 new 分配的内存。然而,在调用 delete[] 后仍然可以访问数组元素。我调用了两次 delete[] 来确认我是否会得到一个 double free or corruption 错误,结果我果然得到了这个错误,这意味着内存已被释放。如果内存已经被释放,那么我怎么能够访问数组元素呢?这可能是一个安全问题,如果我将类似密码之类的东西读入堆中,就可能被利用。
int *foo;
foo = new int[100];

for (int i = 0; i < 100; ++i) {
    foo[i] = i+1;
}

cout << foo[90] << endl;
delete[] foo;
cout << foo[90] << endl;

输出结果如下:

91 91

以及

int *foo;
foo = new int[100];

for (int i = 0; i < 100; ++i) {
    foo[i] = i+1;
}

cout << foo[90] << endl;
delete[] foo;
delete[] foo;
cout << foo[90] << endl;

提供

*** 在./a.out'中出错:double free或corruption (top):0x000000000168d010 ***`


1
删除数组只是告诉堆管理器该内存区域再次可供将来使用。它不会立即覆盖内存(因为这样做是低效的)。然而,这意味着该区域随时可能被覆盖(例如,在下一个内存分配时,可能由不同的线程完成),因此您不能再依赖其内容。 - Jeremy Friesner
2
delete 后立即将指针设置为 nullptr 是一个好习惯(如果存在指针可能被解引用的风险)。这可以减轻意外访问已释放内存(以及双重删除)的风险。 - Sander De Dycker
1
关于安全问题:如果攻击者能够访问您进程的内存空间,则可能存在安全问题。但是,当然,如果攻击者能够读取您的内存空间,那么无论如何,您都可能会遇到麻烦,因为攻击者可以在您删除数组之前在调试器中停止您的进程,并在此时读取密码。话虽如此,出于安全考虑,一些程序确实会手动清零敏感内存并采取其他额外步骤。 - Jeremy Friesner
1
一个有趣的问题和测试,但是你没有说明你期望发生什么,以及为什么你会期望那样。null值?某种“访问被拒绝”的异常导致进程停止?编译器警告你在删除内存之前没有清除它吗? - 2785528
2
@scott 注意,将指针设置为null只有在解除引用检查null的情况下才有助于防止意外解除引用的风险。 - eerorika
显示剩余2条评论
1个回答

5
内存被释放,这意味着它不再被归属了,但编译器不会花费额外的精力把它每次删除时擦除回到0。
在您访问它之前,编译器也不会检查内存是否正确分配 - 这会降低性能,并假定您不这样做。(虽然像valgrind或调试器这样的工具可以检测到这些错误的调用)
因此,它只是在内部将内存范围更改为“未分配”,这意味着另一个对new的调用可以使用相同的内存范围。然后该内存中的任何数据都将被覆盖,并且foo[90]将不再返回相同的内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接