首页 文章

Visual Studio对删除的指针做了什么?为什么?

提问于
浏览
128

我读过的一本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 回答

  • 10

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

    这看起来很奇怪,所以我做了一点挖掘,发现这个Microsoft blog post包含一个讨论"Automated pointer sanitization when deleting C++ objects"的部分 .

    ...检查NULL是一种常见的代码构造,这意味着对NULL的现有检查与使用NULL作为清理值相结合可能偶然隐藏真正的内存安全问题,其根本原因确实需要解决 . 出于这个原因,我们选择了0x8123作为清理值 - 从操作系统的角度来看,这与零地址(NULL)在同一个内存页面中,但是在0x8123处的访问冲突将更好地脱颖而出,因为需要更详细的关注 .

    它不仅解释了Visual Studio在删除后对指针的作用,还解释了为什么他们选择不自动将其设置为 NULL


    "feature"作为"SDL checks"设置的一部分启用 . 要启用/禁用它,请转到: PROJECT -> Properties -> Configuration Properties -> C/C++ -> General -> SDL checks

    要确认这一点:

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

    ptr = 007CBC10
    ptr = 007CBC10
    

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

    Visual Studio可以通过在设计中记录这个缺陷来为您设置一个棘手的情况 .

  • 30

    您会看到 /sdl 编译选项的副作用 . 默认情况下,对于VS2015项目,它启用了除/ gs提供的安全检查之外的其他安全检查 . 使用项目>属性> C / C>常规> SDL检查设置进行更改 .

    引用MSDN article

    执行有限的指针清理 . 在不涉及解除引用的表达式中以及没有用户定义的析构函数的类型中,在调用delete之后,指针引用被设置为无效地址 . 这有助于防止重用过时的指针引用 .

    请记住,在使用MSVC时,将删除的指针设置为NULL是一种不好的做法 . 它会破坏您从Debug Heap和this / sdl选项获得的帮助,您无法再在程序中检测到无效的免费/删除调用 .

  • 171

    它还指出指针将继续指向同一位置,直到重新分配或设置为NULL .

    这绝对是误导性的信息 .

    显然,当调用delete时,指针指向的地址会发生变化!为什么会这样?这是否与Visual Studio有关?

    这显然属于语言规范 . ptr 在调用 delete 后无效 . 在 delete 之后使用 ptr 是造成未定义行为的原因 . Don't do it. 在调用 delete 之后,运行时环境可以随意使用 ptr 执行任何操作 .

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

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

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

    一般来说,即使阅读(就像你上面所做的那样,注意:这与...不同)解除引用)无效指针的值(指针变为无效,例如当你 delete 它)是实现定义的行为 . 这是在CWG #1438中引入的 . 另见here .

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

  • 0

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

    为了提高效率并避免给出错误的安全概念,指针通常不会在 delete 内更改 . 将删除指针设置为预定义值在大多数复杂方案中都不会有效,因为被删除的指针很可能只是指向此位置的指针之一 .

    事实上,我越是想到它,就像往常一样,我发现VS在这样做时有更多的错误 . 如果指针是const怎么办?它还会改变它吗?

  • 1

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

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

    在早些时候,MS将值设置为 0xcfffffff .

相关问题