C++对象模型_Class Obj作为函数参数

开发环境
  • VC6.0
  • 编辑器 Cmd Markdown
  1. 关于C/C++中基本类型(如:int,int*等)作为函数参数时,是通过将该变量的值压栈来进行参数传递;本文通过C++反汇编代码分析了当对象作为函数参数时(该形参非引用或指针),参数如何传递以及此时栈帧的结构。

  2. 对象作为函数参数时,参数传递过程(如:函数的声明为:void show(class Object obj);该函数的调用的为show(arg);其中实参arg的类型为class Object):1,在栈顶上为obj对象分配内存空间,然后将对象arg的首地址压栈;2,调用拷贝构造函数(此为C++中三种调用拷贝构造函数情况之一),将arg的数据成员拷贝至obj;3,执行show()函数体(此时,ebp+8即为obj的首地址)。

  3. 具体分析过程,见代码注释。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//C++源码。
//VC6.0
#include<iostream>
#includestdio>
using namespace std;
class CBase{
int i;
public:
CBase(int i=0)
{
this->i=i;
}
CBase(CBase& rhs) //拷贝构造函数。
{
i=rhs.i;
printf("拷贝构造函数=%d\n",i);
}
void show(CBase B1, CBase B2) //对象作为形参。
{
printf("show=%d,%d\n",B1.i,B2.i);
}
};
int main()
{
CBase Base;
CBase Basex(1);
CBase Basexx(2);
Base.show(Basex,Basexx);
// printf("this is an end!\n");
}
---------------------------------------------------------------------------------
//main函数对应的汇编代码。
int main()
24: {
0040D4A0 push ebp
0040D4A1 mov ebp,esp
0040D4A3 sub esp,54h
0040D4A6 push ebx
0040D4A7 push esi
0040D4A8 push edi
0040D4A9 lea edi,[ebp-54h]
0040D4AC mov ecx,15h
0040D4B1 mov eax,0CCCCCCCCh
0040D4B6 rep stos dword ptr [edi]
25: CBase Base;
0040D4B8 push 0
0040D4BA lea ecx,[ebp-4] //ecx保存的是对象Base的this指针。
0040D4BD call @ILT+0(CBase::CBase) (00401005)//调用构造函数
26: CBase Basex(1);
0040D4C2 push 1
0040D4C4 lea ecx,[ebp-8] //ecx保存的是对象Basex的this指针。
0040D4C7 call @ILT+0(CBase::CBase) (00401005)//调用构造函数
27: CBase Basexx(2);
0040D4CC push 2
0040D4CE lea ecx,[ebp-0Ch] //ecx保存的是对象Basexx的this指针。
0040D4D1 call @ILT+0(CBase::CBase) (00401005)//调用构造函数
28: Base.show(Basex,Basexx);
0040D4D6 push ecx //等价于esp=esp-4;
0040D4D7 mov ecx,esp //ecx保存的是show函数的形参B2的this指针。
0040D4D9 lea eax,[ebp-0Ch]
0040D4DC push eax //对Basexx的this(即Basexx对象的首地址)指针压栈。
0040D4DD call @ILT+10(CBase::CBase) (0040100f)//调用拷贝构造函数
0040D4E2 push ecx //等价于esp=esp-4;
0040D4E3 mov ecx,esp //ecx保存的是show函数的形参B1的this指针。
0040D4E5 lea edx,[ebp-8]
0040D4E8 push edx //对Basex的this(即Basex对象的首地址)指针压栈。
0040D4E9 call @ILT+10(CBase::CBase) (0040100f)//调用拷贝构造函数
0040D4EE lea ecx,[ebp-4] //ecx保存的是Base对象的this指针。
0040D4F1 call @ILT+20(CBase::show) (00401019)//调用show函数。
29: printf("this is an end!\n");
0040D4F6 push offset string "this is an end!\n" (00422fa4)
0040D4FB call printf (0040d7c0)
0040D500 add esp,4
30: }
0040D503 pop edi
0040D504 pop esi
0040D505 pop ebx
0040D506 add esp,54h
0040D509 cmp ebp,esp
0040D50B call __chkesp (004010b0)
0040D510 mov esp,ebp
0040D512 pop ebp
0040D513 ret
//show函数对应的汇编代码。
16: void show(CBase B1,CBase B2)
17: {
0040D550 push ebp
0040D551 mov ebp,esp
0040D553 sub esp,44h
0040D556 push ebx
0040D557 push esi
0040D558 push edi
0040D559 push ecx
0040D55A lea edi,[ebp-44h]
0040D55D mov ecx,11h
0040D562 mov eax,0CCCCCCCCh
0040D567 rep stos dword ptr [edi]
0040D569 pop ecx
0040D56A mov dword ptr [ebp-4],ecx
18: printf("show=%d,%d\n",B1.i,B2.i);
0040D56D mov eax,dword ptr [ebp+0Ch] //ebp+0Ch对应的是B2的首地址。
0040D570 push eax
0040D571 mov ecx,dword ptr [ebp+8] //ebp+8h对应的是B1的首地址。
0040D574 push ecx
0040D575 push offset string "show=%d,%d\n" (00422fcc)
0040D57A call printf (0040d7c0)
0040D57F add esp,0Ch
19: }
0040D582 pop edi
0040D583 pop esi
0040D584 pop ebx
0040D585 add esp,44h
0040D588 cmp ebp,esp
0040D58A call __chkesp (004010b0)
0040D58F mov esp,ebp
0040D591 pop ebp
0040D592 ret 8
//拷贝构造函数对应的汇编代码(注释略)。
11: CBase(CBase& rhs)
0040D840 push ebp
0040D841 mov ebp,esp
0040D843 sub esp,44h
0040D846 push ebx
0040D847 push esi
0040D848 push edi
0040D849 push ecx
0040D84A lea edi,[ebp-44h]
0040D84D mov ecx,11h
0040D852 mov eax,0CCCCCCCCh
0040D857 rep stos dword ptr [edi]
0040D859 pop ecx
0040D85A mov dword ptr [ebp-4],ecx
12: {
13: i=rhs.i;
0040D85D mov eax,dword ptr [ebp-4]
0040D860 mov ecx,dword ptr [ebp+8]
0040D863 mov edx,dword ptr [ecx]
0040D865 mov dword ptr [eax],edx
14: printf("拷贝构造函数=%d\n",i);
0040D867 mov eax,dword ptr [ebp-4]
0040D86A mov ecx,dword ptr [eax]
0040D86C push ecx
0040D86D push offset string "\xbf\xbd\xb1\xb4\xb9\xb9\xd4\xec\xba\xaf\xca\xfd=%d\n" (00422fb8)
0040D872 call printf (0040d7c0)
0040D877 add esp,8
15: }
0040D87A mov eax,dword ptr [ebp-4]
0040D87D pop edi
0040D87E pop esi
0040D87F pop ebx
0040D880 add esp,44h
0040D883 cmp ebp,esp
0040D885 call __chkesp (004010b0)
0040D88A mov esp,ebp
0040D88C pop ebp
0040D88D ret 4
  1. 当执行show函数作用域内代码时,栈结构图示如下:

stack