粉丝的观察
粉丝说,申请了基本类型的数组,但是用delete,在VS下居然没问题。
嗯,这个粉丝观察很仔细,但为什么会这样?这个视频我们一起来看看为什么。

示例代码
#include <iostream>
int main()
{
int* p1 = new int[3]{ 1,2,3 };
delete p1;
int* p2 = new int[3]{ 4,5,6 };
delete[]p2;
}
仍然是几行非常简单的C++代码,p1, p2都是一个包含3个Int类型的数组。
但是p1采用单对象 delete来释放,但是p2采用数组 delete来释放,咱们看看它们到底有什么区别。
单对象delete的汇编实现
还是看汇编代码,前边这些是p1内存分配,咱们就跳过它,直接看delete p1;的汇编实现。
008121C5 mov eax,dword ptr [p1]
这一行读出p1的指针。
008121C8 mov dword ptr [ebp-0ECh],eax
然后把它放到临时槽中。
008121CE push 4
这一行压入参数 4,这是C++14的sized delete 形参,4指的是单对象大小,也就是sizeof(int)的大小。
请注意这里3个int理论应该是12,但是它传的大小只是4。
008121D0 mov ecx,dword ptr [ebp-0ECh]
008121D6 push ecx
然后这2行压入待释放指针参数,也是做为delete的第一个参数传入。
008F21D7 call operator delete (08F10A0h)
紧接着调用 单对象版 operator delete,从汇编代码可以看出调用这个 单对象版的delete传入了2个参数,第一个参数是待释放的内存指针,第2个参数是4。
operator delete函数分析
void __CRTDECL operator delete(void* const block, size_t const)
可以看到operator delete的确是有2个参数。
好咱们看看delete函数的实现:
008F2FF0 push ebp
008F2FF1 mov ebp,esp
前边这里是常规函数序言,用于建立栈帧。
008F2FF3 mov eax,dword ptr [block]
008F2FF6 push eax
008F2FF7 call operator delete (08F10D7h)
接下来它会调用 operator delete,但是它只传了一个参数block,也就是待释放内存地址。
看到了吗,它忽略了第二个参数 size_t。
单参数operator delete实现
void __CRTDECL operator delete(void* const block) noexcept
{
00813B30 push ebp
00813B31 mov ebp,esp
#ifdef _DEBUG
_free_dbg(block, _UNKNOWN_BLOCK);
00813B33 push 0FFFFFFFFh
00813B35 mov eax,dword ptr [block]
00813B38 push eax
00813B39 call __free_dbg (0811393h)
00813B3E add esp,8
#else
free(block);
#endif
}
可以看到,这个delete函数只接受一个参数,在debug模式下会调用_free_dbg而在release模式下会调用free来对内存进行释放。
数组delete[]的汇编实现
好,咱们再来看看P2指针相关的内存操作实现,这些内存申请操作也直接跳过,直接着看看数组delete的汇编代码实现。
00812264 mov eax,dword ptr [p2]
这一行读取p2的指针。
00812267 mov dword ptr [ebp-104h],eax
然后把它保存到一个临时槽。
0081226D mov ecx,dword ptr [ebp-104h]
00812273 push ecx
然后压入待释放指针。
008F2274 call operator delete[] (08F1320h)
这里是调用 数组版 operator delete[]。
到这里我们可以知道在调用单对象版 operator delete传入了2个参数,而调用数组版 operator delete时则只传入一个参数。
数组版operator delete[]分析
void __CRTDECL operator delete[](void* const block)
可以看到 数组版operator delete的汇编代码实现和单对象版的operator delete数据实现差不多是一样的,它只接受一个block参数,之后会把这个参数传给 operator delete。
结论与注意事项
现在,我们就能知道,对 POD 类型,在MS VS中的实现其实是一样的,这也是视频开头粉丝说的不会出错的原因。
即然如此,是不是说,对于传统数据类型,用new数组申请的内存,就可以使用delete来释放了呢?
仍然不建议这么做的,因为对于常规数据类型:在 MSVS的默认实现中,看起来相同。但是在不同编译器或者配置下(特别是启用数组 cookie、调试信息、对齐优化时)则有可能会改变布局,混用时可能会导致内存越界或 heap corruption。



