如果_heapchk()返回&#34,我可以假设堆没有损坏吗?好的"?

时间:2011-02-22 10:09:39

标签: c++ visual-c++ memory-management undefined-behavior

在Visual C ++中使用new[]分配一个对象数组,然后delete(不是delete[])默认情况下triggers undefined behavior of the following kind

为所有对象调用析构函数需要知道对象的数量,因此Visual C ++调用::operator new[]()来分配稍大的缓冲区,将元素的数量放入开头,然后调用构造函数并返回指向第一个的指针宾语。完成delete后,它只会销毁第一个对象,然后将错误的指针传递给::operator delete(),这个指针的执行方式与默认情况下的::operator delete[]()完全相同。

如果以下

class Class {
public:
   ~Class() { Sleep( 0 );}
};

delete new Class[1];

在Release配置中编译并在调试器下运行,程序以断点停止:

ntdll.dll!_DbgBreakPoint@0()    
ntdll.dll!_RtlpBreakPointHeap@4()  + 0x28 bytes 
ntdll.dll!_RtlpValidateHeapEntry@12()  + 0x113 bytes    
ntdll.dll!_RtlDebugFreeHeap@12()  + 0x97 bytes  
ntdll.dll!_RtlFreeHeapSlowly@12()  + 0x246cf bytes  
ntdll.dll!_RtlFreeHeap@12()  + 0x17646 bytes    
sample.exe!free(void * pBlock=0x0003339c)  Line 110 C
sample.exe!main()  Line 48  C++
sample.exe!__tmainCRTStartup()  Line 266 + 0x12 bytes   C
kernel32.dll!_BaseProcessStart@4()  + 0x23 bytes

看起来像堆腐败 - 至少这是我在这种情况下所期望的。

我尝试在该行之前和之后调用_heapchk(),并且令人惊讶的是它两次都返回_HEAPOK。我也是enabled memory leak detection,并且在程序结束时没有报告泄漏。

如果_heapchk()返回_HEAPOK,我可以假设堆完好无损吗?

4 个答案:

答案 0 :(得分:3)

来自the documentation(强调我的):

  

_heapchk函数通过检查最小的堆一致性来帮助调试与堆相关的问题。

它不会检查绝对一致性和正确性:它应该能够检测到主要的,令人震惊的错误,但它不会捕获每个错误。

答案 1 :(得分:2)

我不能肯定地说,但我已经玩了一点,并注意到两件事:

  1. 在发布版本中,即使C ++代码使用常规删除,编译器有时也会对“operator delete []”进行汇编调用。
  2. 在发布版本中,当使用错误删除时,CRT会识别不匹配,并通过RtlpLogHeapFailure报告(上帝知道这意味着什么......)。
  3. 我只是在这里推测,但我的猜测是处理堆的删除部分比你想象的要聪明。它实际上有一种方法来识别你发送它的内容,并相应地对待它。因此,在Debug构建中,您将从其他调试测试中获得断言,但在较低级别上,堆仍然可以保护自身,并执行正确的操作,而不会损坏。因此,如果情况确实如此,那么在VC ++上,删除类型只会影响析构,而堆部分则以任何一种方式完成。其他编译器可能表现不同。

    这就是_heapchk()不会失败的原因......

答案 2 :(得分:1)

不,_heapchk返回_HEAPOK只是意味着找不到问题或者堆不支持验证。

我会说当你收到这个错误时,你更有可能删除一个未初始化的指针。

答案 3 :(得分:1)

您的意思是在调用未定义的行为后调用函数,并且仍然想要信任结果? ; - )

在这里我只是猜测未定义的行为可以通过很多方式表现出来,最常见的是“似乎工作”。惊讶?

其他不寻常的情况是:
- 你在同一个声明中调用new和delete - 您只分配一个元素 - 你只在整个计划中的一个地方做 - 析构函数对内存分配没有任何重要作用 所有这些都会影响优化器生成代码的方式,正如eran所说 - 编译器可以很容易地看到所有这些并实现您正在做的事情。这不是傻瓜!