假设有个类Test , 定义了Test *p = new Test; 之后却用delete []p 去释放内存,就会导致程序崩溃或者直接进入无限循环。
分析如下:一段测试的代码
#include<iostream>
using namespace std;
class Test
{
public :
int m_a;
~Test() {};
};
void main()
{
Test *p = new Test;
delete []p;
cout << "end";
}
这段代码在VS的Debug/Release模式下,程序陷入循环卡死并不打印end,也不异常退出,对delete下断分析:
002322C8 mov eax,dword ptr [p] //eax = 对象的指针
002322CB mov dword ptr [ebp-0ECh],eax //保存对象指针到ebp-0xEC
002322D1 mov ecx,dword ptr [ebp-0ECh] //ecx = 对象指针
002322D7 mov dword ptr [ebp-0E0h],ecx //0xe0保存对象指针
002322DD cmp dword ptr [ebp-0E0h],0 //判断是否为NULL
002322E4 je main+8Bh (02322FBh)
002322E6 push 3 //释放标志,delete[]为3,delete为1
002322E8 mov ecx,dword ptr [ebp-0E0h] //ecx = 对象指针
002322EE call Test::`vector deleting destructor' //delete函数
002322F3 mov dword ptr [ebp-0F4h],eax
002322F9 jmp main+95h (0232305h)
跟进去delete destructor函数再看看:
00232612 mov eax,dword ptr [ebp+8] //eax = 释放标记
00232615 and eax,2 //判断释放为3
00232618 je (023265Eh)
0023261A push offset Test::~Test (0231492h) //push析构函数地址
0023261F mov eax,dword ptr [this] //eax = 对象地址
00232622 mov ecx,dword ptr [eax-4] //new[],对象地址前4字节为对象个数
00232625 push ecx //push对象的个数
00232626 push 4
00232628 mov edx,dword ptr [this]
0023262B push edx //push 对象首地址
0023262C call (0231276h) //析构代理函数
00232631 mov eax,dword ptr [ebp+8]
00232634 and eax,1 //看释放标记为1则跳
00232637 je (0232656h)
00232639 mov eax,dword ptr [this] //对象首地址
0023263C mov ecx,dword ptr [eax-4] //new []保存个数的地方
0023263F lea edx,[ecx*4+4] //4+个数*大小 = 总内存大小
00232646 push edx //push 释放空间总大小
00232647 mov eax,dword ptr [this] //eax = 对象首地址
0023264A sub eax,4 //在new[]下申请的地址要-4
0023264D push eax //push申请的内存地址
0023264E call operator delete[] (0231037h) //call delete[]
00232653 add esp,8
可以看到如果我们程序误用delete[][来删除非new []的对象时候,会将我们对象的前四字节当作对象个数来传入析构代理函数,这里再debug模式下没用用的堆空间为0xFDFDFDFD,所以程序就一直循环,卡死这里
如果这里通过后,下面delete[]仍然会将对象地址-4以及对象[]的总大小进行堆空间的释放,此时就会出现堆空间释放粗偶,导致程序崩溃。