Visual Studio 对删除的指针有何作用?为什么?

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

我一直在阅读的一本 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....

很明显指针指向的地址在调用delete时发生了变化!

为什么会这样?这与 Visual Studio 具体有关吗?

如果 delete 无论如何都可以更改它指向的地址,为什么 delete 不自动将指针设置为 NULL 而不是某个随机地址?

我相信,你是 运行 某种调试模式,VS 正试图将你的指针重新指向某个已知位置,以便可以跟踪和报告进一步取消引用它的尝试。在发布模式下尝试 compiling/running 相同的程序。

指针通常不会在 delete 内部更改,以提高效率并避免给出错误的安全概念。将删除指针设置为预定义值在大多数复杂情况下都没有好处,因为被删除的指针很可能只是指向该位置的几个指针之一。

其实越想越觉得是VS的错,照例如此。如果指针是 const 怎么办?还会改吗?

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?

这显然在语言规范之内。 ptr 在调用 delete 后无效。在 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???

将指针的值更改为任何旧值都在语言规范内。至于将其更改为 NULL,我会说,那会很糟糕。如果指针的值设置为 NULL,程序将以更理智的方式运行。但是,这将隐藏问题。当程序在不同的优化设置下编译或移植到不同的环境时,问题很可能会在最不合时宜的时候出现。

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

这看起来很奇怪,所以我做了一点挖掘,发现这个 Microsoft blog post 包含一个讨论“删除 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 检查”设置的一部分启用。对于 enable/disable,它转到:PROJECT -> Properties -> Configuration Properties -> C/C++ -> General -> SDL checks

确认一下:

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

ptr = 007CBC10
ptr = 007CBC10

“feature”在引号中是因为在您有两个指向同一位置的指针的情况下,调用 delete 只会清除其中的 ONE 个。另一个将指向无效位置...


更新:

经过 5 年以上的 C++ 编程经验,我意识到这整个问题基本上是一个有争议的问题。如果您是一名 C++ 程序员并且仍在使用 newdelete 来管理原始指针而不是使用智能指针(这会绕过整个问题),您可能需要考虑改变职业道路以成为C程序员。 ;)

您会看到 /sdl 编译选项的副作用。默认情况下为 VS2015 项目打开,它启用除 /gs 提供的那些之外的额外安全检查。使用 Project > Properties > C/C++ > General > SDL checks setting 来改变它。

引用自MSDN article

  • 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 时,将已删除的指针设置为 NULL 是一种不好的做法。它破坏了您从调试堆和此 /sdl 选项获得的帮助,您无法再检测程序中的无效 free/delete 调用。

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

通常甚至 读取(就像你上面做的那样,注意:这与取消引用不同)无效指针的值(例如当你 delete它)是实现定义的行为。这是在 CWG #1438. See also .

中介绍的

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

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

进行测试时,它可能会隐藏一个错误

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

在较早的时候,MS 将该值设置为 0xcfffffff