【问题标题】:Can I assume heap was not corrupted if _heapchk() returns "okay"?如果 _heapchk() 返回“okay”,我可以假设堆没有损坏吗?
【发布时间】:2011-02-22 10:09:39
【问题描述】:

在 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,我可以假设堆是完整的吗?

【问题讨论】:

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


    【解决方案1】:

    来自the documentation(强调我的):

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

    它不检查绝对的一致性和正确性:它应该能够检测出严重的严重错误,但它不会捕获所有错误。

    【讨论】:

      【解决方案2】:

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

      1. 在发布版本中,即使 C++ 代码使用常规删除,编译器有时也会对“operator delete[]”进行汇编调用。
      2. 在发布版本中,当使用错误的删除时,CRT 会识别出不匹配,并通过 RtlpLogHeapFailure 报告它(天知道这意味着什么......)。

      我只是在这里推测,但我的猜测是处理堆的删除部分比你想象的要聪明。它实际上有一种方法可以识别您发送的内容,并相应地处理它。因此,在 Debug 构建中,您将从额外的调试测试中获得断言,但在较低级别上,堆仍然可以保护自己并执行正确的操作,而不会损坏。因此,如果情况确实如此,那么在 VC++ 上,删除的类型只会影响破坏,而堆部分无论哪种方式都是正确的。其他编译器的行为可能有所不同。

      这就是为什么 _heapchk() 不会返回失败...

      【讨论】:

        【解决方案3】:

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

        我会说,当您收到此错误时,您更有可能正在删除未初始化的指针。

        【讨论】:

          【解决方案4】:

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

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

          其他异常情况有:
          - 在同一个语句中调用 new 和 delete。
          - 你只分配一个元素。
          - 您只需在整个程序中的一个地方执行此操作。
          - 析构函数对内存分配没有任何意义。
          所有这些都会影响优化器生成代码的方式,正如 Eran 所说 - 编译器可以很容易地看到所有这些并意识到你在做什么。不傻!

          【讨论】:

            猜你喜欢
            • 2019-07-25
            • 2016-04-22
            • 1970-01-01
            • 1970-01-01
            • 2012-06-13
            • 1970-01-01
            • 2011-09-29
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多