我读过的一本C书指出,当使用 delete
运算符删除指针时,指向它的位置的内存是"freed",它可以被覆盖 . 它还指出指针将继续指向同一位置,直到它被重新分配或设置为 NULL
.
但是在Visual Studio 2012中;这似乎不是这样的!
Example:
#include <iostream>
using namespace std;
int main()
{
int* ptr = new int;
cout << "ptr = " << ptr << endl;
delete ptr;
cout << "ptr = " << ptr << endl;
system("pause");
return 0;
}
当我编译并运行该程序时,我得到以下输出:
ptr = 0050BC10
ptr = 00008123
Press any key to continue....
显然,当调用delete时,指针指向的地址会发生变化!
为什么会这样?这是否与Visual Studio有关?
如果删除可以改变它指向的地址,为什么不删除自动设置指针 NULL
而不是一些随机地址?
6 回答
我注意到
ptr
中存储的地址总是被00008123
覆盖...这看起来很奇怪,所以我做了一点挖掘,发现这个Microsoft blog post包含一个讨论"Automated pointer sanitization when deleting C++ objects"的部分 .
它不仅解释了Visual Studio在删除后对指针的作用,还解释了为什么他们选择不自动将其设置为
NULL
!"feature"作为"SDL checks"设置的一部分启用 . 要启用/禁用它,请转到: PROJECT -> Properties -> Configuration Properties -> C/C++ -> General -> SDL checks
要确认这一点:
更改此设置并重新运行相同的代码会产生以下输出:
"feature"在引号中,因为在您有两个指向同一位置的指针的情况下,调用delete只会清理其中一个 . 另一个将指向无效位置 .
Visual Studio可以通过在设计中记录这个缺陷来为您设置一个棘手的情况 .
您会看到
/sdl
编译选项的副作用 . 默认情况下,对于VS2015项目,它启用了除/ gs提供的安全检查之外的其他安全检查 . 使用项目>属性> C / C>常规> SDL检查设置进行更改 .引用MSDN article:
请记住,在使用MSVC时,将删除的指针设置为NULL是一种不好的做法 . 它会破坏您从Debug Heap和this / sdl选项获得的帮助,您无法再在程序中检测到无效的免费/删除调用 .
这绝对是误导性的信息 .
这显然属于语言规范 .
ptr
在调用delete
后无效 . 在delete
之后使用ptr
是造成未定义行为的原因 . Don't do it. 在调用delete
之后,运行时环境可以随意使用ptr
执行任何操作 .将指针的值更改为任何旧值都在语言规范内 . 至于把它改成NULL,我会说,那会很糟糕 . 如果指针的值设置为NULL,程序将以更合理的方式运行 . 但是,这将隐藏问题 . 当程序使用不同的优化设置进行编译或移植到不同的环境时,问题可能会出现在最不合时宜的时刻 .
一般来说,即使阅读(就像你上面所做的那样,注意:这与...不同)解除引用)无效指针的值(指针变为无效,例如当你
delete
它)是实现定义的行为 . 这是在CWG #1438中引入的 . 另见here .请注意,在读取无效指针的值之前是未定义的行为,所以上面的内容将是未定义的行为,这意味着任何事情都可能发生 .
我相信,您正在运行某种调试模式,并且VS正在尝试将指针重新指向某个已知位置,以便可以跟踪和报告进一步尝试取消引用它 . 尝试在发布模式下编译/运行相同的程序 .
为了提高效率并避免给出错误的安全概念,指针通常不会在
delete
内更改 . 将删除指针设置为预定义值在大多数复杂方案中都不会有效,因为被删除的指针很可能只是指向此位置的指针之一 .事实上,我越是想到它,就像往常一样,我发现VS在这样做时有更多的错误 . 如果指针是const怎么办?它还会改变它吗?
删除指针后,它指向的内存可能仍然有效 . 要显示此错误,指针值将设置为明显的值 . 这确实有助于调试过程 . 如果该值设置为
NULL
,则它可能永远不会显示为程序流中的潜在错误 . 因此,当您稍后针对NULL
进行测试时,它可能会隐藏错误 .另一点是,某些运行时优化器可能会检查该值并更改其结果 .
在早些时候,MS将值设置为
0xcfffffff
.