今天,在看《Effective C++》书籍的时候,看到const的在作为函数参数传递时的应用,一般函数参数的传递有三种方式,passed by pointer-to-const、passed by reference-to-const和passed by value,相信对这三种方式都应该比较熟悉,普通的内置类型三者的效率都差不多,但对于用户自定义的类型,比如struct和class这些,使用前两种的效率就要高一些了。还有就是前两种的使用是改变实参本身的值,而后一种则只是改变实参副本的值,对实参本身并没有改变。下面举一个简单的交换两个数的函数,我想通过这个例子应该能够说明问题。
2 #include <cstdio>
3 using namespace std;
4
5 void SwapValue(int a, int b)
6 {
7 int temp = 0;
8 temp = a;
9 a = b;
10 b = temp;
11 }
12
13 void SwapPointer(int *a, int *b)
14 {
15 int temp = 0;
16 temp = *a;
17 *a = *b;
18 *b = temp;
19 }
20
21 void SwapReference(int& a, int &b)
22 {
23 int temp = 0;
24 temp = a;
25 a = b;
26 b = temp;
27 }
28
29 int main()
30 {
31 int n = 5, m = 6;
32 SwapValue(n, m);
33 printf("%d %d\n", n, m);
34
35 SwapPointer(&n, &m);
36 printf("%d %d\n", n, m);
37
38 SwapReference(n, m);
39 printf("%d %d\n", n, m);
40
41 return 0;
42 }
43
VC编译之后在调试中查看其反汇编后的内容,只列出相关的代码。
这里说明一下,对于参数和局部变量来说,一般是通过EBP寄存器来进行访问的。而且参数一般是EBP+x的形式,而局部变量一般是EBP – x的形式。
116: int n = 5, m = 6;
00401158 C7 45 FC 05 00 00 00 mov dword ptr [ebp-4],5
0040115F C7 45 F8 06 00 00 00 mov dword ptr [ebp-8],6
这两条汇编指令是对main函数内的局部变量n和m进行初始化操作
117: SwapValue(n, m);
00401166 8B 45 F8 mov eax,dword ptr [ebp-8]
00401169 50 push eax
0040116A 8B 4D FC mov ecx,dword ptr [ebp-4]
0040116D 51 push ecx
0040116E E8 AB FE FF FF call @ILT+25(SwapValue) (0040101e)
00401173 83 C4 08 add esp,8
从上面的反汇编代码可以看出,如果参数使用的是passed by value的形式,参数的内容是先放进去寄存器中,而在函数内部改变的仅仅是寄存器的值,即是参数副本的值,对参数本身并没有改变。
120: SwapPointer(&n, &m);
0040118B 8D 4D F8 lea ecx,[ebp-8]
0040118E 51 push ecx
0040118F 8D 55 FC lea edx,[ebp-4]
00401192 52 push edx
00401193 E8 81 FE FF FF call @ILT+20(SwapPointer) (00401019)
00401198 83 C4 08 add esp,8
这段代码是把参数的地址压入栈中,此时改变地址的内容也就是改变参数的值了。
123: SwapReference(n, m);
004011B0 8D 55 F8 lea edx,[ebp-8]
004011B3 52 push edx
004011B4 8D 45 FC lea eax,[ebp-4]
004011B7 50 push eax
004011B8 E8 48 FE FF FF call @ILT+0(SwapReference) (00401005)
004011BD 83 C4 08 add esp,8
这段代码跟上一段是相同的。可以看出两者都是将参数的地址压入栈中,即在函数内部改变了参数的值。
SwapValue函数
92: int temp = 0;
00401068 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
93: temp = a;
0040106F 8B 45 08 mov eax,dword ptr [ebp+8]
00401072 89 45 FC mov dword ptr [ebp-4],eax
94: a = b;
00401075 8B 4D 0C mov ecx,dword ptr [ebp+0Ch]
00401078 89 4D 08 mov dword ptr [ebp+8],ecx
95: b = temp;
0040107B 8B 55 FC mov edx,dword ptr [ebp-4]
0040107E 89 55 0C mov dword ptr [ebp+0Ch],edx
96: }
SwapPointer函数
100: int temp = 0;
004010B8 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
101: temp = *a;
004010BF 8B 45 08 mov eax,dword ptr [ebp+8]
004010C2 8B 08 mov ecx,dword ptr [eax]
004010C4 89 4D FC mov dword ptr [ebp-4],ecx
102: *a = *b;
004010C7 8B 55 08 mov edx,dword ptr [ebp+8]
004010CA 8B 45 0C mov eax,dword ptr [ebp+0Ch]
004010CD 8B 08 mov ecx,dword ptr [eax]
004010CF 89 0A mov dword ptr [edx],ecx
103: *b = temp;
004010D1 8B 55 0C mov edx,dword ptr [ebp+0Ch]
004010D4 8B 45 FC mov eax,dword ptr [ebp-4]
004010D7 89 02 mov dword ptr [edx],eax
104: }
SwapReference函数
108: int temp = 0;
00401108 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
109: temp = a;
0040110F 8B 45 08 mov eax,dword ptr [ebp+8]
00401112 8B 08 mov ecx,dword ptr [eax]
00401114 89 4D FC mov dword ptr [ebp-4],ecx
110: a = b;
00401117 8B 55 08 mov edx,dword ptr [ebp+8]
0040111A 8B 45 0C mov eax,dword ptr [ebp+0Ch]
0040111D 8B 08 mov ecx,dword ptr [eax]
0040111F 89 0A mov dword ptr [edx],ecx
111: b = temp;
00401121 8B 55 0C mov edx,dword ptr [ebp+0Ch]
00401124 8B 45 FC mov eax,dword ptr [ebp-4]
00401127 89 02 mov dword ptr [edx],eax
112: }
参数使用passed by pointer和passed by reference两者反汇编出来的代码是一样的,且传给函数的是参数本身的地址。所以对地址内容的改变也就是对参数的改变。