- 注册时间
- 2011-3-6
- 最后登录
- 1970-1-1
该用户从未签到
|
有这么一段C++代码:
[cpp] view plaincopyprint?
01.#include <cstdio>
02.
03.class CExample {
04. int x, y;
05.
06.public:
07. CExample(int va, int vb) {
08. x = va;
09. y = vb;
10. }
11. CExample(const CExample& C) {
12. x = C.x;
13. y = C.y;
14. }
15. ~CExample() {
16. printf("~CExample()\n");
17. }
18.
19. void Show () {
20. printf("%d, %d\n", x, y);
21. return ;
22. }
23.};
24.
25.void fun(CExample E) {
26. printf("In F(CExample)\n");
27. return ;
28.}
29.
30.int main() {
31. CExample A(100, 200);
32. CExample B = A;
33. B.Show();
34. fun(A);
35. return 0;
36.}
#include <cstdio>
class CExample {
int x, y;
public:
CExample(int va, int vb) {
x = va;
y = vb;
}
CExample(const CExample& C) {
x = C.x;
y = C.y;
}
~CExample() {
printf("~CExample()\n");
}
void Show () {
printf("%d, %d\n", x, y);
return ;
}
};
void fun(CExample E) {
printf("In F(CExample)\n");
return ;
}
int main() {
CExample A(100, 200);
CExample B = A;
B.Show();
fun(A);
return 0;
}
运行结果:
编译调试的环境是 VC 6.0,注释如下:
main 函数:
[plain] view plaincopyprint?
01.30: int main() {
02.00401130 push ebp
03.00401131 mov ebp,esp
04.00401133 push 0FFh
05.00401135 push offset __ehhandler$_main (00413422)
06.0040113A mov eax,fs:[00000000]
07.00401140 push eax
08.00401141 mov dword ptr fs:[0],esp
09.00401148 sub esp,5Ch
10.0040114B push ebx
11.0040114C push esi
12.0040114D push edi
13.0040114E lea edi,[ebp-68h]
14.00401151 mov ecx,17h
15.00401156 mov eax,0CCCCCCCCh
16.0040115B rep stos dword ptr [edi] ; 以上代码为建立堆栈,EBP = 1245000
17.31: CExample A(100, 200);
18.0040115D push 0C8h ; 将 200 压入堆栈中
19.00401162 push 64h ; 将 100 压入堆栈中
20.00401164 lea ecx,[ebp-14h] ; EBP-14h 是 A 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中
21.00401167 call @ILT+0(CExample::CExample) (00401005) ; 调用构造函数 CExample(int, int)
22.0040116C mov dword ptr [ebp-4],0
23.32: CExample B = A;
24.00401173 lea eax,[ebp-14h] ; 将 A 的内存地址保存到 EAX 寄存器中
25.00401176 push eax ; 将 EAX 压入堆栈中
26.00401177 lea ecx,[ebp-1Ch] ; EBP-1Ch 是 B 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中
27.0040117A call @ILT+20(CExample::CExample) (00401019) ; 调用拷贝构造函数,将A中的值复制到 B 的内存空间中,
28.
29. ; 细心观察一下栈地址就能明白
30.0040117F mov byte ptr [ebp-4],1
31.33: B.Show();
32.00401183 lea ecx,[ebp-1Ch]
33.00401186 call @ILT+25(CExample::Show) (0040101e)
34.34: fun(A);
35.0040118B sub esp,8 ; ESP = 1244884, ESP - 8 = 1244876;ESP-8 是开辟的一块内存,
36. ; 在调用fun之前,先调用拷贝构造函数对 A 进行拷贝,拷贝的内容就
37. ; 存放在这块内存空间中(一个CExample对象,记为Temp)
38.0040118E mov ecx,esp ; ECX 寄存器保存 Temp 对象的内存地址
39.00401190 mov dword ptr [ebp-20h],esp ; EBP-20h 这块空间保存 Temp 对象的内存地址
40.00401193 lea edx,[ebp-14h] ; EDX 寄存器保存 A 对象的内存地址
41.00401196 push edx ; ESP = 1244872,将 A 的内存地址压入栈
42.00401197 call @ILT+20(CExample::CExample) (00401019) ; ESP = 1244868,函数返回地址压入栈,调用拷贝构造函数
43.0040119C mov dword ptr [ebp-28h],eax ; ESP = 1244876, EBP - 28h = 1244876,保存 Temp 的内存地址
44.0040119F call @ILT+15(fun) (00401014) ; ESP = 1244872,函数返回地址压入栈,调用fun函数
45.004011A4 add esp,8 ; __cdecl 规则,恢复堆栈段,EBP = 1245000
46.35: return 0;
47.004011A7 mov dword ptr [ebp-24h],0
48.004011AE mov byte ptr [ebp-4],0
49.004011B2 lea ecx,[ebp-1Ch] ; EBP - 1Ch 是 B 的内存地址,保存到 ECX 中
50.004011B5 call @ILT+5(CExample::~CExample) (0040100a) ; 调用析构函数,销毁对象 B
51.004011BA mov dword ptr [ebp-4],0FFFFFFFFh
52.004011C1 lea ecx,[ebp-14h] ; EBP - 14h 是 A 的内存地址,保存到 ECX 中
53.004011C4 call @ILT+5(CExample::~CExample) (0040100a) ; 调用析构函数,销毁对象 A
54.004011C9 mov eax,dword ptr [ebp-24h]
55.36: }
30: int main() {
00401130 push ebp
00401131 mov ebp,esp
00401133 push 0FFh
00401135 push offset __ehhandler$_main (00413422)
0040113A mov eax,fs:[00000000]
00401140 push eax
00401141 mov dword ptr fs:[0],esp
00401148 sub esp,5Ch
0040114B push ebx
0040114C push esi
0040114D push edi
0040114E lea edi,[ebp-68h]
00401151 mov ecx,17h
00401156 mov eax,0CCCCCCCCh
0040115B rep stos dword ptr [edi]; 以上代码为建立堆栈,EBP = 1245000
31: CExample A(100, 200);
0040115D push 0C8h; 将 200 压入堆栈中
00401162 push 64h; 将 100 压入堆栈中
00401164 lea ecx,[ebp-14h]; EBP-14h 是 A 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中
00401167 call @ILT+0(CExample::CExample) (00401005); 调用构造函数 CExample(int, int)
0040116C mov dword ptr [ebp-4],0
32: CExample B = A;
00401173 lea eax,[ebp-14h]; 将 A 的内存地址保存到 EAX 寄存器中
00401176 push eax; 将 EAX 压入堆栈中
00401177 lea ecx,[ebp-1Ch]; EBP-1Ch 是 B 的内存地址,占 8 个字节,将该地址保存到 ECX 寄存器中
0040117A call @ILT+20(CExample::CExample) (00401019); 调用拷贝构造函数,将A中的值复制到 B 的内存空间中,
; 细心观察一下栈地址就能明白
0040117F mov byte ptr [ebp-4],1
33: B.Show();
00401183 lea ecx,[ebp-1Ch]
00401186 call @ILT+25(CExample::Show) (0040101e)
34: fun(A);
0040118B sub esp,8; ESP = 1244884, ESP - 8 = 1244876;ESP-8 是开辟的一块内存,
; 在调用fun之前,先调用拷贝构造函数对 A 进行拷贝,拷贝的内容就
; 存放在这块内存空间中(一个CExample对象,记为Temp)
0040118E mov ecx,esp; ECX 寄存器保存 Temp 对象的内存地址
00401190 mov dword ptr [ebp-20h],esp; EBP-20h 这块空间保存 Temp 对象的内存地址
00401193 lea edx,[ebp-14h]; EDX 寄存器保存 A 对象的内存地址
00401196 push edx; ESP = 1244872,将 A 的内存地址压入栈
00401197 call @ILT+20(CExample::CExample) (00401019); ESP = 1244868,函数返回地址压入栈,调用拷贝构造函数
0040119C mov dword ptr [ebp-28h],eax; ESP = 1244876, EBP - 28h = 1244876,保存 Temp 的内存地址
0040119F call @ILT+15(fun) (00401014); ESP = 1244872,函数返回地址压入栈,调用fun函数
004011A4 add esp,8; __cdecl 规则,恢复堆栈段,EBP = 1245000
35: return 0;
004011A7 mov dword ptr [ebp-24h],0
004011AE mov byte ptr [ebp-4],0
004011B2 lea ecx,[ebp-1Ch]; EBP - 1Ch 是 B 的内存地址,保存到 ECX 中
004011B5 call @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 B
004011BA mov dword ptr [ebp-4],0FFFFFFFFh
004011C1 lea ecx,[ebp-14h]; EBP - 14h 是 A 的内存地址,保存到 ECX 中
004011C4 call @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁对象 A
004011C9 mov eax,dword ptr [ebp-24h]
36: }
拷贝构造函数 CExample(const CExample& C):
[plain] view plaincopyprint?
01.11: CExample(const CExample& C) {
02.00401270 push ebp
03.00401271 mov ebp,esp
04.00401273 sub esp,44h
05.00401276 push ebx
06.00401277 push esi
07.00401278 push edi
08.00401279 push ecx
09.0040127A lea edi,[ebp-44h]
10.0040127D mov ecx,11h
11.00401282 mov eax,0CCCCCCCCh
12.00401287 rep stos dword ptr [edi] ; 以上为建立堆栈代码,EBP = 1244864
13.00401289 pop ecx ; 恢复 ECX 寄存器的内容,即为 Temp 的内存地址
14.0040128A mov dword ptr [ebp-4],ecx ; EBP - 4 = 1244860,这块栈内存保存 Temp 的内存地址
15.12: x = C.x;
16.0040128D mov eax,dword ptr [ebp-4] ; EBP - 4 = 1244860,EAX 获得 Temp 的内存地址(EAX 指向 Temp)
17.00401290 mov ecx,dword ptr [ebp+8] ; EBP + 8 = 1244872,ECX 获得 A 的内存地址(ECX 指向 A)
18.00401293 mov edx,dword ptr [ecx] ; EDX = 100(A 的首地址是 100,100 后于 200 进栈)
19.00401295 mov dword ptr [eax],edx ; Temp.x = 100
20.13: y = C.y;
21.00401297 mov eax,dword ptr [ebp-4]
22.0040129A mov ecx,dword ptr [ebp+8]
23.0040129D mov edx,dword ptr [ecx+4] ; EDX = 200
24.004012A0 mov dword ptr [eax+4],edx ; 同理,Temp.y = 200
25.14: }
26.004012A3 mov eax,dword ptr [ebp-4] ; EAX 寄存器保存 Temp 的内存空间
27.004012A6 pop edi
28.004012A7 pop esi
29.004012A8 pop ebx
30.004012A9 mov esp,ebp
31.004012AB pop ebp ; 恢复EBP,EBP = 1245000
32.004012AC ret 4 ; 恢复这一函数的堆栈段
11: CExample(const CExample& C) {
00401270 push ebp
00401271 mov ebp,esp
00401273 sub esp,44h
00401276 push ebx
00401277 push esi
00401278 push edi
00401279 push ecx
0040127A lea edi,[ebp-44h]
0040127D mov ecx,11h
00401282 mov eax,0CCCCCCCCh
00401287 rep stos dword ptr [edi]; 以上为建立堆栈代码,EBP = 1244864
00401289 pop ecx; 恢复 ECX 寄存器的内容,即为 Temp 的内存地址
0040128A mov dword ptr [ebp-4],ecx; EBP - 4 = 1244860,这块栈内存保存 Temp 的内存地址
12: x = C.x;
0040128D mov eax,dword ptr [ebp-4]; EBP - 4 = 1244860,EAX 获得 Temp 的内存地址(EAX 指向 Temp)
00401290 mov ecx,dword ptr [ebp+8]; EBP + 8 = 1244872,ECX 获得 A 的内存地址(ECX 指向 A)
00401293 mov edx,dword ptr [ecx]; EDX = 100(A 的首地址是 100,100 后于 200 进栈)
00401295 mov dword ptr [eax],edx; Temp.x = 100
13: y = C.y;
00401297 mov eax,dword ptr [ebp-4]
0040129A mov ecx,dword ptr [ebp+8]
0040129D mov edx,dword ptr [ecx+4]; EDX = 200
004012A0 mov dword ptr [eax+4],edx; 同理,Temp.y = 200
14: }
004012A3 mov eax,dword ptr [ebp-4]; EAX 寄存器保存 Temp 的内存空间
004012A6 pop edi
004012A7 pop esi
004012A8 pop ebx
004012A9 mov esp,ebp
004012AB pop ebp; 恢复EBP,EBP = 1245000
004012AC ret 4; 恢复这一函数的堆栈段
fun 函数:
[plain] view plaincopyprint?
01.25: void fun(CExample E) {
02.00401050 push ebp
03.00401051 mov ebp,esp
04.00401053 push 0FFh
05.00401055 push offset __ehhandler$?fun@@YAXVCExample@@@Z (004133f9)
06.0040105A mov eax,fs:[00000000]
07.00401060 push eax
08.00401061 mov dword ptr fs:[0],esp
09.00401068 sub esp,40h
10.0040106B push ebx
11.0040106C push esi
12.0040106D push edi
13.0040106E lea edi,[ebp-4Ch]
14.00401071 mov ecx,10h
15.00401076 mov eax,0CCCCCCCCh
16.0040107B rep stos dword ptr [edi]
17.0040107D mov dword ptr [ebp-4],0 ; EBP = 1244868,以上为建立堆栈代码
18.26: printf("In F(CExample)\n");
19.00401084 push offset string "In F(CExample)\n" (0042501c)
20.00401089 call printf (00401320)
21.0040108E add esp,4
22.27: return ;
23.00401091 mov dword ptr [ebp-4],0FFFFFFFFh
24.00401098 lea ecx,[ebp+8] ; EBP+8 = 1244876,是 Temp 的内存地址
25. ; ECX 寄存器保存
26.0040109B call @ILT+5(CExample::~CExample) (0040100a) ; 调用析构函数,销毁 Temp 对象(生命期结束)
27.28: }
28.004010A0 mov ecx,dword ptr [ebp-0Ch]
29.004010A3 mov dword ptr fs:[0],ecx
30.004010AA pop edi
31.004010AB pop esi
32.004010AC pop ebx
33.004010AD add esp,4Ch
34.004010B0 cmp ebp,esp
35.004010B2 call __chkesp (00401780)
36.004010B7 mov esp,ebp
37.004010B9 pop ebp ; 恢复EBP,EBP = 1245000
38.004010BA ret
25: void fun(CExample E) {
00401050 push ebp
00401051 mov ebp,esp
00401053 push 0FFh
00401055 push offset __ehhandler$?fun@@YAXVCExample@@@Z (004133f9)
0040105A mov eax,fs:[00000000]
00401060 push eax
00401061 mov dword ptr fs:[0],esp
00401068 sub esp,40h
0040106B push ebx
0040106C push esi
0040106D push edi
0040106E lea edi,[ebp-4Ch]
00401071 mov ecx,10h
00401076 mov eax,0CCCCCCCCh
0040107B rep stos dword ptr [edi]
0040107D mov dword ptr [ebp-4],0; EBP = 1244868,以上为建立堆栈代码
26: printf("In F(CExample)\n");
00401084 push offset string "In F(CExample)\n" (0042501c)
00401089 call printf (00401320)
0040108E add esp,4
27: return ;
00401091 mov dword ptr [ebp-4],0FFFFFFFFh
00401098 lea ecx,[ebp+8]; EBP+8 = 1244876,是 Temp 的内存地址
; ECX 寄存器保存
0040109B call @ILT+5(CExample::~CExample) (0040100a); 调用析构函数,销毁 Temp 对象(生命期结束)
28: }
004010A0 mov ecx,dword ptr [ebp-0Ch]
004010A3 mov dword ptr fs:[0],ecx
004010AA pop edi
004010AB pop esi
004010AC pop ebx
004010AD add esp,4Ch
004010B0 cmp ebp,esp
004010B2 call __chkesp (00401780)
004010B7 mov esp,ebp
004010B9 pop ebp; 恢复EBP,EBP = 1245000
004010BA ret
下面重点分析一下调用 fun 函数时程序的工作机制:
执行“fun(A);”时,首先先调用拷贝构造函数,创建一个实体对象Temp,占8个字节长度的栈空间,内容是拷贝 A 的内容;然后再把对象 Temp 的内存地址压入 fun 函数的堆栈中,调用 fun 函数,当 fun 函数结束时,调用析构函数销毁 Temp 对象,从此 Temp 对象所占的栈空间被回收 |
|