关于c ++:Visual Studio对删除的指针做了什么,为什么?

What does Visual Studio do with a deleted pointer and why?

我曾经读过的一本C++书,说明当使用EDCOX1×0操作符删除指针时,它指向的位置的内存被"释放",并且可以被重写。它还声明指针将继续指向同一位置,直到重新分配或设置为NULL

然而,在Visual Studio 2012中,情况似乎并非如此!

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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;
}

当我编译和运行这个程序时,我得到如下输出:

1
2
3
ptr = 0050BC10
ptr = 00008123
Press any key to continue....

显然,当调用delete时指针指向的地址会发生更改!

为什么会这样?这与Visual Studio有什么特别的关系吗?

如果删除可以改变它指向的地址,为什么不自动将指针设置为NULL而不是某个随机地址呢?


我注意到存储在ptr中的地址总是被00008123覆盖…

这看起来很奇怪,所以我做了一点挖掘,发现这个微软博客帖子包含了一个章节,讨论"删除C++对象时的自动指针消毒"。

...checks for NULL are a common code construct meaning that an existing check for NULL combined with using NULL as a sanitization value could fortuitously hide a genuine memory safety issue whose root cause really does needs addressing.

For this reason we have chosen 0x8123 as a sanitization value – from an operating system perspective this is in the same memory page as the zero address (NULL), but an access violation at 0x8123 will better stand out to the developer as needing more detailed attention.

它不仅解释了删除指针后Visual Studio如何处理指针,还回答了为什么他们选择不自动将其设置为NULL

此"功能"作为"SDL检查"设置的一部分启用。启用/禁用它:项目->属性>配置属性> > C/C++ > > -> SDL检查

要确认这一点:

更改此设置并重新运行同一代码将产生以下输出:

1
2
ptr = 007CBC10
ptr = 007CBC10

"feature"用引号括起来,因为在同一位置有两个指针的情况下,调用delete只会清除其中一个指针。另一个将左指无效位置。

Visual Studio可能会因未能在其设计中记录此缺陷而使您陷入困境。


您可以看到/sdl编译选项的副作用。在VS2015项目的默认情况下,它启用了比/GS提供的安全检查更多的安全检查。使用Projject > Projt> C/C++>通用> SDL检查设置以更改它。

引用来自msdn文章:

  • Performs limited pointer sanitization. In expressions that do not involve dereferences and in types that have no user-defined destructor, pointer references are set to a non-valid address after a call to delete. This helps to prevent the reuse of stale pointer references.

请记住,在使用msvc时,将删除的指针设置为空是一种糟糕的做法。它会破坏您从调试堆和这个/sdl选项中获得的帮助,您将无法再在程序中检测到无效的空闲/删除调用。


It also states that the pointer will continue to point to the same location until it is reassigned or set to NULL.

这绝对是误导信息。

Clearly the address that the pointer is pointing to changes when delete is called!

Why is this happening? Does this have something to do with Visual Studio specifically?

这显然在语言规范中。调用delete后,ptr无效。在deleted之后使用ptr是导致未定义行为的原因。不要这样做。在调用delete之后,运行时环境可以自由地使用ptr做它想做的任何事情。

And if delete can change the address it is pointing to anyways, why wouldn't delete automatically set the pointer to NULL instead of some random address???

将指针的值更改为任何旧值都在语言规范中。如果把它改成空的话,我会说,那就太糟糕了。如果指针的值设置为空,程序的行为将更加正常。然而,这将隐藏问题。当使用不同的优化设置编译程序或将程序移植到不同的环境时,问题很可能会在最不合适的时刻出现。


1
2
delete ptr;
cout <<"ptr =" << ptr << endl;

一般来说,甚至读取(如上面所述,注意:这与取消引用不同)无效指针的值(指针变为无效,例如,当deleteit)是实现定义的行为时。这是在CWG 1438中引入的。这里也可以看到。

请注意,在读取无效指针的值之前是未定义的行为,所以上面的内容是未定义的行为,这意味着任何事情都可能发生。


我相信,您正在运行某种调试模式,而vs正试图将指针重新指向某个已知位置,以便进一步尝试取消对它的引用,从而跟踪并报告。尝试在发布模式下编译/运行同一程序。

为了提高效率和避免错误的安全性,通常不会在delete内更改指针。在大多数复杂的场景中,将删除指针设置为预定义的值是没有用的,因为被删除的指针可能只是指向此位置的几个指针中的一个。

事实上,我想得越多,我就越发现vs在做这件事的时候是错误的,就像往常一样。如果指针是常量呢?它还会改变吗?


删除指针后,它指向的内存可能仍然有效。为了显示这个错误,指针值被设置为一个明显的值。这确实有助于调试过程。如果该值被设置为NULL,它可能永远不会在程序流中显示为潜在的bug。因此,当您稍后对NULL进行测试时,它可能会隐藏一个bug。

另一点是,一些运行时优化器可能会检查该值并更改其结果。

在早期,ms将值设置为0xcfffffff