代码:
#include<iostream> using namespace std; int AddTwoNumber(int a,int b=8);//设置形参默认值 int AddTwoNumber(int *,int *); int AddTwoNum(int &,int &); int main() { int a=2, b=8; int result = AddTwoNumber(2,b);// AddTwoNumber(&a,&b); AddTwoNum(a,b); return 0; } int AddTwoNumber(int a,int b) { return a+b; } int AddTwoNumber(int *pa,int *pb) { return *pa+*pb; } int AddTwoNum(int &a,int &b) { return a+b; }
在Visual C++ 6.0中编译,链接后,用OD载入,按照第一课的方法找到Main函数入口,F7跟进,来到主函数体内
00401050 /> \55 push ebp ; 保存ebp
00401051 |. 8BEC mov ebp, esp ;
00401053 |. 83EC 4C sub esp, 4C ;在堆栈中预留空间
00401056 |. 53 push ebx
00401057 |. 56 push esi
00401058 |. 57 push edi
00401059 |. 8D7D B4 lea edi, dword ptr [ebp-4C]
0040105C |. B9 13000000 mov ecx, 13
00401061 |. B8 CCCCCCCC mov eax, CCCCCCCC
00401066 |. F3:AB rep stos dword ptr es:[edi]
00401068 |. C745 FC 02000>mov dword ptr [ebp-4], 2 ; 变量定义以及初始化
0040106F |. C745 F8 08000>mov dword ptr [ebp-8], 8
00401076 |. 8B45 F8 mov eax, dword ptr [ebp-8]
00401079 |. 50 push eax
0040107A |. 6A 02 push 2
0040107C |. E8 8EFFFFFF call 0040100F ;调用AddTwoNumber(int a,int b) ,传值调用
00401081 |. 83C4 08 add esp, 8
00401084 |. 8945 F4 mov dword ptr [ebp-C], eax
00401087 |. 8D4D F8 lea ecx, dword ptr [ebp-8]
0040108A |. 51 push ecx
0040108B |. 8D55 FC lea edx, dword ptr [ebp-4]
0040108E |. 52 push edx
0040108F |. E8 76FFFFFF call 0040100A ;调用int AddTwoNumber(int *pa,int *pb) 传址调用
00401094 |. 83C4 08 add esp, 8
00401097 |. 8D45 F8 lea eax, dword ptr [ebp-8]
0040109A |. 50 push eax
0040109B |. 8D4D FC lea ecx, dword ptr [ebp-4]
0040109E |. 51 push ecx
0040109F |. E8 61FFFFFF call 00401005 ;调用int AddTwoNum(int &a,int &b)引用调用
004010A4 |. 83C4 08 add esp, 8
004010A7 |. 33C0 xor eax, eax
004010A9 |. 5F pop edi
004010AA |. 5E pop esi
004010AB |. 5B pop ebx
004010AC |. 83C4 4C add esp, 4C
004010AF |. 3BEC cmp ebp, esp
004010B1 |. E8 AA710000 call 00408260
004010B6 |. 8BE5 mov esp, ebp
004010B8 |. 5D pop ebp
004010B9 \. C3 retn
函数参数调用方式:
一:
传值调用: 函数调用时,实参的值传递给形参使用;
1)程序中定义为:
int AddTwoNumber(int a,int b)
{
return a+b;
}
2)反汇编后的代码为:
参数入栈:
00401079 |. 50 push eax
0040107A |. 6A 02 push 2
函数实现:
004010E0 /> \55 push ebp
004010E1 |. 8BEC mov ebp, esp
004010E3 |. 83EC 40 sub esp, 40
004010E6 |. 53 push ebx
004010E7 |. 56 push esi
004010E8 |. 57 push edi
004010E9 |. 8D7D C0 lea edi, dword ptr [ebp-40]
004010EC |. B9 10000000 mov ecx, 10
004010F1 |. B8 CCCCCCCC mov eax, CCCCCCCC
004010F6 |. F3:AB rep stos dword ptr es:[edi]
004010F8 |. 8B45 08 mov eax, dword ptr [ebp+8]
004010FB |. 0345 0C add eax, dword ptr [ebp+C]
004010FE |. 5F pop edi
004010FF |. 5E pop esi
00401100 |. 5B pop ebx
00401101 |. 8BE5 mov esp, ebp
00401103 |. 5D pop ebp
00401104 \. C3 retn
二:
传址调用:形参为指针,实参为地址
1)程序中定义为:
int AddTwoNumber(int *pa,int *pb)
{
return *pa+*pb;
}
2)反汇编后的代码为:
参数入栈:
00401081 |. 83C4 08 add esp, 8 ;调用者平衡被调用者的参数堆栈
00401084 |. 8945 F4 mov dword ptr [ebp-C], eax ;int result = AddTwoNumber();
00401087 |. 8D4D F8 lea ecx, dword ptr [ebp-8] ;注意这里,指针调用
0040108A |. 51 push ecx
0040108B |. 8D55 FC lea edx, dword ptr [ebp-4]
0040108E |. 52 push edx
函数实现:
00401110 /> \55 push ebp
00401111 |. 8BEC mov ebp, esp
00401113 |. 83EC 40 sub esp, 40
00401116 |. 53 push ebx
00401117 |. 56 push esi
00401118 |. 57 push edi
00401119 |. 8D7D C0 lea edi, dword ptr [ebp-40]
0040111C |. B9 10000000 mov ecx, 10
00401121 |. B8 CCCCCCCC mov eax, CCCCCCCC
00401126 |. F3:AB rep stos dword ptr es:[edi]
00401128 |. 8B45 08 mov eax, dword ptr [ebp+8]
0040112B |. 8B00 mov eax, dword ptr [eax]
0040112D |. 8B4D 0C mov ecx, dword ptr [ebp+C]
00401130 |. 0301 add eax, dword ptr [ecx] ;在ecx寄存器上右键,数据窗口中跟随,地址是堆栈中的地址;
00401132 |. 5F pop edi
00401133 |. 5E pop esi
00401134 |. 5B pop ebx
00401135 |. 8BE5 mov esp, ebp
00401137 |. 5D pop ebp
00401138 \. C3 retn
引用调用:形参为引用,实参为变量名
1)程序中定义为:
int AddTwoNum(int &a,int &b)
{
return a+b;
}
2)反汇编后的代码为:
参数入栈:
00401097 |. 8D45 F8 lea eax, dword ptr [ebp-8]
0040109A |. 50 push eax
0040109B |. 8D4D FC lea ecx, dword ptr [ebp-4]
0040109E |. 51 push ecx
函数实现:
00401150 /> \55 push ebp
00401151 |. 8BEC mov ebp, esp
00401153 |. 83EC 40 sub esp, 40
00401156 |. 53 push ebx
00401157 |. 56 push esi
00401158 |. 57 push edi
00401159 |. 8D7D C0 lea edi, dword ptr [ebp-40]
0040115C |. B9 10000000 mov ecx, 10
00401161 |. B8 CCCCCCCC mov eax, CCCCCCCC
00401166 |. F3:AB rep stos dword ptr es:[edi]
00401168 |. 8B45 08 mov eax, dword ptr [ebp+8]
0040116B |. 8B00 mov eax, dword ptr [eax]
0040116D |. 8B4D 0C mov ecx, dword ptr [ebp+C]
00401170 |. 0301 add eax, dword ptr [ecx]
00401172 |. 5F pop edi
00401173 |. 5E pop esi
00401174 |. 5B pop ebx
00401175 |. 8BE5 mov esp, ebp
00401177 |. 5D pop ebp
00401178 \. C3 retn
从上述我们可以看出,三种函数调用方式上 ,传值调用和后两种在参数的使用和功能实现上都有不同,由于是指针,
所以在参数入栈时用了lea指令,并且,传址调用和引用调用的功能性代码完全相同,而且,用引用调用能简化书写,
它们的不同,在这个程序中,唯一的区别就是参数入栈时,使用的寄存器不同;由于使用了Lea指令,传址调用和引用调用
函数执行的时间肯定要比传值调用的时间长,所以如果是在函数的递归调用和嵌套调用中,如果为了效力考虑,在完成
相同的功能的函数设计上,尽量使用传值调用;另外在传址调用和引用调用上,建议大家使用引用调用,因为它们在实现
上一样的,而引用调用使用起来更加方便;
并且有一点想让大家注意,大家在想栈这个数据结构,不要想着Push, pop后栈中的数据就不再了,
物理内存一般为动态RAM,一般为MOS型,它具有客观实在性,因此,即使你pop平衡了栈后,原来的数据仍然存放在内存中
也就是,除非断电或再一次被使用,否则内存中的数据不会改变