new/delete操作符的汇编级粗略过程
MSVC6.0 DEBUG ASM
默认情况下(没有重载new运算符) new int 结果上完全等价于 (int *)malloc(sizeof(int))。new int[n]完全等价于(int *)malloc(sizeof(int) * n) 这个可以推广到所有非类类型。 对于类,假设ClassA,new ClassA; 相当于(ClassA *)malloc(sizeof(ClassA))之后用返回的指针调用ClassA的默认构造函数,再返回那个指针 new ClassA(参数表) 基本上和上面一样,只是会调用参数表对应的构造方法 new ClassA[n]比较复杂,一般会进行类似 int *t = (int *)malloc(sizeof(ClassA) * n + sizeof(int)); *t = n; ClassA *r = (ClassA *)(t + 1); for (int i = 0; i < n; i++, (r++)->ClassA::ClassA()); return (ClassA *)(t + 1);的操作。简单说就是申请内存,保存个数,然后对每个对象调用构造函数 对于非类类型 delete p和delete[] p相同,等价于free(p) 对于类,假设还是ClassA,delete p等价于 p->ClassA::~ClassA(); free(p); 先调用析构函数,然后释放内存 对于delete[] p,并且p用new []申请。对应上面的new []实现,delete[] p会进行 int *t = (int *)p - 1; for (int i = 0; i < *t; i++, (p++)->ClassA::~ClassA()); free(t);的操作。注:以上这段摘自百度知道http://zhidao.baidu.com/question/200118329.html中的其他答案。。。
(一)内置类型。
Ⅰ.new 内置类型:(如int *a=new int;或int *b=new int[5])
1.将operator new函数的参数nSize压栈,通常等于待分配空间长度(Eg: 1或5)。
2.调用void* __cdecl operator new(size_t nSize),寄存器传递分配成功后的指针,主调函数用临时变量接收。(注1:函数调用方式__cdecl表明参数压栈顺序为由右及左,调用完毕后由主调函数清除入栈参数恢复栈顶。注2:operator new函数执行过程中有机制用于记录分配的长度。该函数具体过程也不简单,参见其他网页)
3.将返回值传给目标指针。
Ⅱ.delete 内置类型:(如delete a;或delete[] a;;delete b;或delete[] b;,效果均相同)(注:若类类型无析构函数,则其delete操作符与内置类型完全相同!)
1.将待处理指针p压栈。
2.调用
void __cdecl operator delete(void* p)
。(注:调用方式也是__cdecl。因operator new函数执行过程中有用于记录分配的长度的外部数据结构,故在delete过程中可获取该指针对应分配的空间长度,从而正确释放。调用方式加不加中括号都是同样效果!)
(二)类类型。
分两种类别讨论:有无自定义构造函数和有无析构函数。以下条目是有构造函数和析构函数时的过程。
若去掉析构函数,与有析构函数相比:new单个对象的过程相同;但new对象数组则不同,虽过程相近,但分配区直接是类对象数组,而没有记录元素个数的单元。
若去掉析构函数,则new等于operator new直接按所需空间分配,然后初始化;而delete与内置类型完全相同,无需析构,delete操作符可混用!
若去掉构造函数但有析构函数,则调用operator new后无需初始化,但new对象数组时分配空间时仍多用一个内存单元记录个数。
若构造析构均无,则退化为内置类型。
(注:对于在函数中定义的局部变量,其过程相对以下条目,只是少了operator new/delete函数的调用)
Ⅲ.
new 类类型的单个对象:(如int *c=new CString;)
1.将operator new函数的参数nSize压栈,即该类的sizeof值(Eg: 4)。
2.调用void* __cdecl operator new(size_t nSize),寄存器传递分配成功后的指针,主调函数用临时变量接收。
3.将构造函参进栈,将分配所得对象的指针传入ECX(类成员函数的调用方式默认为thiscall方式),调用适当的类构造函数。(若无构造函数,则无此步骤。)
3.将对象指针传给目标指针。
Ⅳ.
new 类类型的数组:(如int *d=new CString[5];)
1.将operator new函数的参数nSize压栈,即该数组的sizeof值+4(Eg: 5*4+4=0x18h)。
2.调用void* __cdecl operator new(size_t nSize),寄存器传递分配成功后的指针,主调函数用临时变量接收。将已分配区的首个单元填上元素个数。(注:分配区的首个内存单元将存储数组元素个数,其后的内存单元才是类对象数组的实际空间,故nSize要大4字节)。
3.将eh vector constructor iterator函数的参数压栈,从左往右分别是首个对象的this指针,sizeof类类型,数组元素个数,类默认构造函数,类析构函数。
调用eh vector constructor iterator全局函数,即矢量构造函数,将每个对象均初始化。(注:析构函数可能是用于对象初始化过程失败后的回滚,即将已构造的对象再析构使之无效。)(若无析构函数,则调用vector constructor iterator全局函数,它有4个参数,不含类析构函数。若无构造函数,则无此步骤。)
4.将首个对象的指针,即已分配区指针的下一单元,传给目标指针。
Ⅴ
.
delete 类类型的单个变量:(如delete c;)
1.将立即数1压栈,将待处理指针p(new操作符返回的对象指针)传给ECX。(注:1代表delete操作符类别delete ;,不含方括号)
2.调用类的
scalar deleting destructor函数
。其具体过程为:
(1)将对象指针传入ECX,调用类析构函数。(2)测试之前入栈的立即数,若为奇数则进行(3),否则跳过并结束。(3)将对象指针压栈,调用void __cdecl operator delete(void* p)。
Ⅵ.delete 类类型的数组:(如delete[] d;)
1.将立即数3压栈,将待处理指针p(new[]操作符返回的首对象指针)传给ECX。(注:3代表delete操作符
类别
delete[];
)
2.调用类的
vector deleting destructor
。其具体过程为:
(1)将对象指针传入ECX,调用类析构函数。(2)测试之前入栈的立即数,若为2或3则从(3)顺序执行;若不满足则不进行逐个析构,只对首个对象调用类析构函数,并跳到(4)。(3)将eh vector destructor iterator全局函数的参数压栈,从左往右分别是首个对象的this指针,sizeof类类型,数组元素个数(从首对象地址的上一内存单元中读出),类析构函数。然后调用该函数。(4)测试之前入栈的立即数,若为奇数则将首对象地址的上一单元地址入栈(因这是实际由operator new所分配空间的首地址),调用void __cdecl operator delete(void* p);若不满足则跳过operator delete的调用。最后EAX均传递operator new函数所分配的空间首地址。
测试代码:
class Two
{
int a;
public:
Two( int b =0) { a = 5; };
~Two( void ) { a= 0; };
};
int main(int argc, char* argv[])
{
char *p0 = new char;
char *p1= new char[10];
delete p0;
delete p1;
Two a;
Two b[3];
Two *p2 = new Two;
Two *p3 = new Two(10);
Two *p4 = new Two[7];
p1[0] = *p0;
delete p2;
delete p3;//delete[] p3;
delete[] p4;//delete p4;
return 0;
}
生成汇编:
PUBLIC ??0Two@@QAE@H@Z ; Two::Two
PUBLIC ??1Two@@QAE@XZ ; Two::~Two
PUBLIC ??_FTwo@@QAEXXZ ; Two::`default constructor closure'
PUBLIC ??_GTwo@@QAEPAXI@Z ; Two::`scalar deleting destructor'
PUBLIC ??_ETwo@@QAEPAXI@Z ; Two::`vector deleting destructor'
PUBLIC _main
EXTRN ??_L@YGXPAXIHP6EX0@Z1@Z:NEAR ; `eh vector constructor iterator'
EXTRN ??_M@YGXPAXIHP6EX0@Z@Z:NEAR ; `eh vector destructor iterator'
EXTRN ??2@YAPAXI@Z:NEAR ; operator new
EXTRN ??3@YAXPAX@Z:NEAR ; operator delete
EXTRN __except_list:DWORD
EXTRN __chkesp:NEAR
EXTRN ___CxxFrameHandler:NEAR
_main PROC NEAR ; COMDAT
; 40 : {
push ebp
mov ebp, esp
push -1
push __ehhandler$_main
mov eax, DWORD PTR fs:__except_list
push eax
mov DWORD PTR fs:__except_list, esp
sub esp, 192 ; 000000c0H
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-204]
mov ecx, 48 ; 00000030H
mov eax, -858993460 ; ccccccccH
rep stosd
; 41 : char *p0 = new char;
push 1
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T297[ebp], eax
mov eax, DWORD PTR $T297[ebp]
mov DWORD PTR _p0$[ebp], eax
; 42 : char *p1= new char[10];
push 10 ; 0000000aH
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T298[ebp], eax
mov ecx, DWORD PTR $T298[ebp]
mov DWORD PTR _p1$[ebp], ecx
; 43 : delete p0;
mov edx, DWORD PTR _p0$[ebp]
mov DWORD PTR $T299[ebp], edx
mov eax, DWORD PTR $T299[ebp]
push eax
call ??3@YAXPAX@Z ; operator delete
add esp, 4
; 44 : delete p1;
mov ecx, DWORD PTR _p1$[ebp]
mov DWORD PTR $T300[ebp], ecx
mov edx, DWORD PTR $T300[ebp]
push edx
call ??3@YAXPAX@Z ; operator delete
add esp, 4
; 45 : Two a;
push 0
lea ecx, DWORD PTR _a$[ebp]
call ??0Two@@QAE@H@Z ; Two::Two
mov DWORD PTR __$EHRec$[ebp+8], 0
; 46 : Two b[3];
push OFFSET FLAT:??1Two@@QAE@XZ ; Two::~Two
push OFFSET FLAT:??_FTwo@@QAEXXZ ; Two::`default constructor closure'
push 3
push 4
lea eax, DWORD PTR _b$[ebp]
push eax
call ??_L@YGXPAXIHP6EX0@Z1@Z ; `eh vector constructor iterator'
mov BYTE PTR __$EHRec$[ebp+8], 1
; 47 : Two *p2 = new Two;
push 4
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T302[ebp], eax
mov BYTE PTR __$EHRec$[ebp+8], 2
cmp DWORD PTR $T302[ebp], 0
je SHORT $L303
push 0
mov ecx, DWORD PTR $T302[ebp]
call ??0Two@@QAE@H@Z ; Two::Two
mov DWORD PTR -120+[ebp], eax
jmp SHORT $L304
$L303:
mov DWORD PTR -120+[ebp], 0
$L304:
mov ecx, DWORD PTR -120+[ebp]
mov DWORD PTR $T301[ebp], ecx
mov BYTE PTR __$EHRec$[ebp+8], 1
mov edx, DWORD PTR $T301[ebp]
mov DWORD PTR _p2$[ebp], edx
; 48 : Two *p3 = new Two(10);
push 4
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T306[ebp], eax
mov BYTE PTR __$EHRec$[ebp+8], 3
cmp DWORD PTR $T306[ebp], 0
je SHORT $L307
push 10 ; 0000000aH
mov ecx, DWORD PTR $T306[ebp]
call ??0Two@@QAE@H@Z ; Two::Two
mov DWORD PTR -124+[ebp], eax
jmp SHORT $L308
$L307:
mov DWORD PTR -124+[ebp], 0
$L308:
mov eax, DWORD PTR -124+[ebp]
mov DWORD PTR $T305[ebp], eax
mov BYTE PTR __$EHRec$[ebp+8], 1
mov ecx, DWORD PTR $T305[ebp]
mov DWORD PTR _p3$[ebp], ecx
; 49 : Two *p4 = new Two[7];
push 32 ; 00000020H
call ??2@YAPAXI@Z ; operator new
add esp, 4
mov DWORD PTR $T310[ebp], eax
mov BYTE PTR __$EHRec$[ebp+8], 4
cmp DWORD PTR $T310[ebp], 0
je SHORT $L311
push OFFSET FLAT:??1Two@@QAE@XZ ; Two::~Two
push OFFSET FLAT:??_FTwo@@QAEXXZ ; Two::`default constructor closure'
mov edx, DWORD PTR $T310[ebp]
mov DWORD PTR [edx], 7
push 7
push 4
mov eax, DWORD PTR $T310[ebp]
add eax, 4
push eax
call ??_L@YGXPAXIHP6EX0@Z1@Z ; `eh vector constructor iterator'
mov ecx, DWORD PTR $T310[ebp]
add ecx, 4
mov DWORD PTR -128+[ebp], ecx
jmp SHORT $L312
$L311:
mov DWORD PTR -128+[ebp], 0
$L312:
mov edx, DWORD PTR -128+[ebp]
mov DWORD PTR $T309[ebp], edx
mov BYTE PTR __$EHRec$[ebp+8], 1
mov eax, DWORD PTR $T309[ebp]
mov DWORD PTR _p4$[ebp], eax
; 50 :
; 51 : p1[0] = *p0;
mov ecx, DWORD PTR _p1$[ebp]
mov edx, DWORD PTR _p0$[ebp]
mov al, BYTE PTR [edx]
mov BYTE PTR [ecx], al
; 52 :
; 53 : delete p2;
mov ecx, DWORD PTR _p2$[ebp]
mov DWORD PTR $T314[ebp], ecx
mov edx, DWORD PTR $T314[ebp]
mov DWORD PTR $T313[ebp], edx
cmp DWORD PTR $T313[ebp], 0
je SHORT $L315
push 1
mov ecx, DWORD PTR $T313[ebp]
call ??_GTwo@@QAEPAXI@Z ; Two::`scalar deleting destructor'
mov DWORD PTR -132+[ebp], eax
jmp SHORT $L316
$L315:
mov DWORD PTR -132+[ebp], 0
$L316:
; 54 : delete p3;//delete[] p3;
mov eax, DWORD PTR _p3$[ebp]
mov DWORD PTR $T318[ebp], eax
mov ecx, DWORD PTR $T318[ebp]
mov DWORD PTR $T317[ebp], ecx
cmp DWORD PTR $T317[ebp], 0
je SHORT $L319
push 1
mov ecx, DWORD PTR $T317[ebp]
call ??_GTwo@@QAEPAXI@Z ; Two::`scalar deleting destructor'
mov DWORD PTR -136+[ebp], eax
jmp SHORT $L320
$L319:
mov DWORD PTR -136+[ebp], 0
$L320:
; 55 : delete[] p4;//delete p4;
mov edx, DWORD PTR _p4$[ebp]
mov DWORD PTR $T322[ebp], edx
mov eax, DWORD PTR $T322[ebp]
mov DWORD PTR $T321[ebp], eax
cmp DWORD PTR $T321[ebp], 0
je SHORT $L323
push 3
mov ecx, DWORD PTR $T321[ebp]
call ??_ETwo@@QAEPAXI@Z ; Two::`vector deleting destructor'
mov DWORD PTR -140+[ebp], eax
jmp SHORT $L324
$L323:
mov DWORD PTR -140+[ebp], 0
$L324:
; 56 : return 0;
mov DWORD PTR $T325[ebp], 0
mov BYTE PTR __$EHRec$[ebp+8], 0
push OFFSET FLAT:??1Two@@QAE@XZ ; Two::~Two
push 3
push 4
lea ecx, DWORD PTR _b$[ebp]
push ecx
call ??_M@YGXPAXIHP6EX0@Z@Z ; `eh vector destructor iterator'
mov DWORD PTR __$EHRec$[ebp+8], -1
lea ecx, DWORD PTR _a$[ebp]
call ??1Two@@QAE@XZ ; Two::~Two
mov eax, DWORD PTR $T325[ebp]
; 57 : }
mov ecx, DWORD PTR __$EHRec$[ebp]
mov DWORD PTR fs:__except_list, ecx
pop edi
pop esi
pop ebx
add esp, 204 ; 000000ccH
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret 0
_TEXT ENDS
; COMDAT ??0Two@@QAE@H@Z
_TEXT SEGMENT
_this$ = -4
??0Two@@QAE@H@Z PROC NEAR ; Two::Two, COMDAT
; 35 : Two( int b =0) { a = 5; };
push ebp
mov ebp, esp
sub esp, 68 ; 00000044H
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-68]
mov ecx, 17 ; 00000011H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 5
mov eax, DWORD PTR _this$[ebp]
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 4
??0Two@@QAE@H@Z ENDP ; Two::Two
_TEXT ENDS
; COMDAT ??1Two@@QAE@XZ
_TEXT SEGMENT
_this$ = -4
??1Two@@QAE@XZ PROC NEAR ; Two::~Two, COMDAT
; 36 : ~Two( void ) { a= 0; };
push ebp
mov ebp, esp
sub esp, 68 ; 00000044H
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-68]
mov ecx, 17 ; 00000011H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR _this$[ebp]
mov DWORD PTR [eax], 0
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
??1Two@@QAE@XZ ENDP ; Two::~Two
_TEXT ENDS
; COMDAT ??_FTwo@@QAEXXZ
_TEXT SEGMENT
_this$ = -4
??_FTwo@@QAEXXZ PROC NEAR ; Two::`default constructor closure', COMDAT
push ebp
mov ebp, esp
sub esp, 68 ; 00000044H
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-68]
mov ecx, 17 ; 00000011H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
push 0
mov ecx, DWORD PTR _this$[ebp]
call ??0Two@@QAE@H@Z ; Two::Two
pop edi
pop esi
pop ebx
add esp, 68 ; 00000044H
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret 0
??_FTwo@@QAEXXZ ENDP ; Two::`default constructor closure'
_TEXT ENDS
; COMDAT ??_ETwo@@QAEPAXI@Z
_TEXT SEGMENT
___flags$ = 8
_this$ = -4
??_ETwo@@QAEPAXI@Z PROC NEAR ; Two::`vector deleting destructor', COMDAT
push ebp
mov ebp, esp
sub esp, 68 ; 00000044H
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-68]
mov ecx, 17 ; 00000011H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov eax, DWORD PTR ___flags$[ebp]
and eax, 2
test eax, eax
je SHORT $L281
push OFFSET FLAT:??1Two@@QAE@XZ ; Two::~Two
mov ecx, DWORD PTR _this$[ebp]
mov edx, DWORD PTR [ecx-4]
push edx
push 4
mov eax, DWORD PTR _this$[ebp]
push eax
call ??_M@YGXPAXIHP6EX0@Z@Z ; `eh vector destructor iterator'
mov ecx, DWORD PTR ___flags$[ebp]
and ecx, 1
test ecx, ecx
je SHORT $L282
mov edx, DWORD PTR _this$[ebp]
sub edx, 4
push edx
call ??3@YAXPAX@Z ; operator delete
add esp, 4
$L282:
mov eax, DWORD PTR _this$[ebp]
sub eax, 4
jmp SHORT $L280
$L281:
mov ecx, DWORD PTR _this$[ebp]
call ??1Two@@QAE@XZ ; Two::~Two
mov eax, DWORD PTR ___flags$[ebp]
and eax, 1
test eax, eax
je SHORT $L284
mov ecx, DWORD PTR _this$[ebp]
push ecx
call ??3@YAXPAX@Z ; operator delete
add esp, 4
$L284:
mov eax, DWORD PTR _this$[ebp]
$L280:
pop edi
pop esi
pop ebx
add esp, 68 ; 00000044H
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret 4
??_ETwo@@QAEPAXI@Z ENDP ; Two::`vector deleting destructor'
_TEXT ENDS
; COMDAT ??_GTwo@@QAEPAXI@Z
_TEXT SEGMENT
___flags$ = 8
_this$ = -4
??_GTwo@@QAEPAXI@Z PROC NEAR ; Two::`scalar deleting destructor', COMDAT
push ebp
mov ebp, esp
sub esp, 68 ; 00000044H
push ebx
push esi
push edi
push ecx
lea edi, DWORD PTR [ebp-68]
mov ecx, 17 ; 00000011H
mov eax, -858993460 ; ccccccccH
rep stosd
pop ecx
mov DWORD PTR _this$[ebp], ecx
mov ecx, DWORD PTR _this$[ebp]
call ??1Two@@QAE@XZ ; Two::~Two
mov eax, DWORD PTR ___flags$[ebp]
and eax, 1
test eax, eax
je SHORT $L287
mov ecx, DWORD PTR _this$[ebp]
push ecx
call ??3@YAXPAX@Z ; operator delete
add esp, 4
$L287:
mov eax, DWORD PTR _this$[ebp]
pop edi
pop esi
pop ebx
add esp, 68 ; 00000044H
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret 4
??_GTwo@@QAEPAXI@Z ENDP ; Two::`scalar deleting destructor'
_TEXT ENDS
END