在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
,我可以假设堆完好无损吗?
答案 0 :(得分:3)
来自the documentation(强调我的):
_heapchk
函数通过检查最小的堆一致性来帮助调试与堆相关的问题。
它不会检查绝对一致性和正确性:它应该能够检测到主要的,令人震惊的错误,但它不会捕获每个错误。
答案 1 :(得分:2)
我不能肯定地说,但我已经玩了一点,并注意到两件事:
我只是在这里推测,但我的猜测是处理堆的删除部分比你想象的要聪明。它实际上有一种方法来识别你发送它的内容,并相应地对待它。因此,在Debug构建中,您将从其他调试测试中获得断言,但在较低级别上,堆仍然可以保护自身,并执行正确的操作,而不会损坏。因此,如果情况确实如此,那么在VC ++上,删除类型只会影响析构,而堆部分则以任何一种方式完成。其他编译器可能表现不同。
这就是_heapchk()不会失败的原因......
答案 2 :(得分:1)
不,_heapchk返回_HEAPOK只是意味着找不到问题或者堆不支持验证。
我会说当你收到这个错误时,你更有可能删除一个未初始化的指针。
答案 3 :(得分:1)
您的意思是在调用未定义的行为后调用函数,并且仍然想要信任结果? ; - )
在这里我只是猜测未定义的行为可以通过很多方式表现出来,最常见的是“似乎工作”。惊讶?
其他不寻常的情况是:
- 你在同一个声明中调用new和delete
- 您只分配一个元素
- 你只在整个计划中的一个地方做
- 析构函数对内存分配没有任何重要作用
所有这些都会影响优化器生成代码的方式,正如eran所说 - 编译器可以很容易地看到所有这些并实现您正在做的事情。这不是傻瓜!