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

时间:2015-10-27 17:19:08

标签: c++ pointers visual-studio-2012 memory-management delete-operator

我一直在阅读的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有关?

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

6 个答案:

答案 0 :(得分:171)

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

这看起来很奇怪,所以我做了一点挖掘,发现这个Microsoft blog post包含一节讨论&#34;删除C ++对象时的自动指针清理&#34;。

  

...检查NULL是一种常见的代码构造,这意味着现有的NULL检查与使用NULL作为清理值相结合,可能偶然地隐藏了一个真正的内存安全问题,其根本原因确实需要解决。

     

出于这个原因,我们选择了0x8123作为清理值 - 从操作系统的角度来看,这与零地址(NULL)在同一个内存页面中,但是0x8123处的访问冲突将更好地向开发人员展示需要更详细的关注。

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

这个&#34;功能&#34;作为&#34; SDL检查&#34;的一部分启用设置。要启用/禁用它,请转到: PROJECT - &gt;属性 - &gt;配置属性 - &gt; C / C ++ - &gt;一般 - &gt; SDL检查

确认一下:

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

ptr = 007CBC10
ptr = 007CBC10

&#34;特征&#34;在引号中,因为在你有两个指向同一位置的指针的情况下,调用delete只会清理它们的 ONE 。另一个将指向无效的位置。

Visual Studio可能会因为未能在设计中记录此缺陷而为您设置棘手的情况。

答案 1 :(得分:30)

您会看到/sdl编译选项的副作用。默认情况下,对于VS2015项目,它启用了除/ gs提供的安全检查之外的其他安全检查。使用项目&gt;属性&gt; C / C ++&gt;一般&gt; SDL检查设置以更改它。

引用MSDN article

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

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

答案 2 :(得分:19)

  

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

这绝对是误导性信息。

  

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

     

为什么会这样?这是否与Visual Studio有关?

这显然属于语言规范。致电ptr后,delete无效。在ptr之后使用delete导致未定义的行为。 不要这样做。运行时环境可以在调用ptrdelete随意执行任何操作。

  

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

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

答案 3 :(得分:10)

W/InstanceID/Rpc: No response android.os.ConditionVariable@129d5713
W/System.err: java.io.IOException: TIMEOUT
W/System.err:   at com.google.android.gms.iid.zzc.zzb(Unknown Source)
W/System.err:   at com.google.android.gms.iid.zzc.zza(Unknown Source)
W/System.err:   at com.google.android.gms.iid.InstanceID.zzc(Unknown Source)
W/System.err:   at com.google.android.gms.iid.InstanceID.getToken(Unknown Source)

一般来说,即使读取(就像你上面那样,注意:这与解除引用不同)无效指针的值(指针变为无效,例如当你delete ptr; cout << "ptr = " << ptr << endl; 时)是实现定义的行为。这是在CWG #1438中引入的。另请参阅here

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

答案 4 :(得分:1)

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

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

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

答案 5 :(得分:0)

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

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

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