Visual Studio如何处理删除的指针,为什么?
我一直在阅读的一本C ++书籍指出,当使用delete
操作符删除一个指针时,它所指向的位置上的内存被“释放”,并且可以被覆盖。 它还指出指针将继续指向相同的位置,直到它被重新分配或设置为NULL
。
但是在Visual Studio 2012中, 这似乎并不是这样!
例:
#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....
显然,指针所指向的地址在调用删除时会发生变化!
为什么发生这种情况? 这与Visual Studio具体有什么关系吗?
如果删除可以改变它指向的地址,为什么不删除自动将指针设置为NULL
而不是一些随机地址?
我注意到,存储在ptr
中的地址总是被00008123
覆盖…
这似乎很奇怪,所以我做了一点挖掘,发现这个微博客post里有一个讨论“删除C ++对象时自动指针清理”的部分。
…检查NULL是一种常见的代码构造,意味着现有的NULL检查与使用NULL作为清理值相结合可能偶然地隐藏真正的内存安全问题,其根本原因确实需要寻址。
因为这个原因,我们select0x8123作为一个消毒值 – 从操作系统的angular度来看,它和零地址(NULL)在同一个内存页面,但是0x8123的访问冲突会更好的体现出来, 。
它不仅解释了Visual Studio在删除指针后所做的工作,还解释了为什么他们select不自动将其设置为NULL
!
此“function”作为“SDL检查”设置的一部分启用。 要启用/禁用它,请转到: PROJECT – >属性 – >configuration属性 – > C / C ++ – >常规 – > SDL检查
为了证实这一点:
更改此设置并重新运行相同的代码将生成以下输出:
ptr = 007CBC10 ptr = 007CBC10
“function”是用引号括起来的,因为在有两个指向同一位置的指针的情况下,调用delete只会消除其中的一个。 另一个将被指向无效的位置。
Visual Studio可以通过在devise中没有logging这个缺陷来设置你的粘性。
您将看到/sdl
编译选项的副作用。 默认情况下启用VS2015项目,它启用超出/ gs提供的安全检查。 使用项目>属性> C / C ++>常规> SDL检查设置来改变它。
从MSDN文章引用:
- 执行有限的指针消毒。 在不涉及解除引用的expression式中,在没有用户定义的析构函数的types中,在删除调用之后,指针引用被设置为非有效地址。 这有助于防止重新使用陈旧的指针引用。
请记住,当您使用MSVC时,将删除的指针设置为NULL是不好的做法。 它无法从Debug Heap和this / sdl选项中获得帮助,您将无法再在程序中检测到无效的自由/删除调用。
它还指出指针将继续指向相同的位置,直到它被重新分配或设置为NULL。
这绝对是误导性的信息。
显然,指针所指向的地址在调用删除时会发生变化!
为什么发生这种情况? 这与Visual Studio具体有什么关系吗?
这显然在语言规范之内。 delete
电话后, ptr
无效。 在delete
d之后使用ptr
会导致未定义的行为。 不要这样做。 在delete
呼叫之后,运行时环境可以随意地使用ptr
来执行任何操作。
如果删除可以改变它指向的地址,为什么不会自动将指针设置为NULL而不是一些随机地址?
将指针的值更改为任何旧值都在语言规范内。 至于将其更改为NULL,我会说,这将是不好的。 如果指针的值被设置为NULL,那么程序的行为将会更加健全。 但是,这将隐藏问题。 当程序编译时使用不同的优化设置或移植到不同的环境时,问题可能会出现在最不合适的时刻。
delete ptr; cout << "ptr = " << ptr << endl;
一般情况下,甚至是读取 (像上面这样做,请注意:这与取消引用不同)无效指针的值(例如,当您delete
它时指针变为无效)是实现定义的行为。 这是在CWG#1438中介绍的。 另见这里 。
请注意,在无效指针的读取值是未定义的行为之前,所以你上面的将是未定义的行为,这意味着可能发生任何事情。
我相信,你正在运行某种debugging模式,而VS正试图将你的指针重新指向某个已知的位置,所以进一步尝试去引用它可以被追踪和报告。 尝试在发行模式下编译/运行相同的程序。
为了提高效率,指针通常不会在内部delete
而被改变,并且避免给出错误的安全想法。 将删除指针设置为预定义值在大多数复杂场景中都不会有效,因为删除的指针可能只是指向此位置的指针之一。
事实上,我越想越想象VS一如既往的错误。 如果指针是const,该怎么办? 它还会改变吗?
删除指针后,指向的内存可能仍然有效。 为了显示这个错误,指针值被设置为一个明显的值。 这真的有助于debugging过程。 如果该值设置为NULL
,则可能永远不会在程序stream中显示为潜在错误。 所以当你在后面testingNULL
时候它可能会隐藏一个bug。
还有一点是,某些运行时优化器可能会检查该值并更改其结果。
在早些时候,MS将该值设置为0xcfffffff
。