函数调用约定:当一个函数被调用时,函数的参数会被传递给被调用的函数和返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡堆栈的,当然还有返回值。
函数调用约定的主要约束事件:
参数传递顺序:
1.从右到左依次入栈:__stdcall,__cdecl,__thiscall
2.从左到右依次入栈:__pascal,__fastcall(__简写为一个_)
清除栈:
1.调用者清除栈。
2.被调用函数返回后清除栈。
在windows中,我们常用的两种调用约定就是:_cdecl &_stdcall,另外在vc中,_stdcall又被定义为:WINAPI CALLBACK
他们的压栈顺序都为从右到左,但是他们的清除栈者是不同的。_cdecl由调用者清除栈,而_stdcall由被调用者清除栈。他们的作用在哪呢?_cdecl主要用到变参函数,例如printf(),被调函数并不知道将来会有多少个参数,所以只能由调用者来清理堆栈;而_stdcall则用于定参数,他把处理权限交予被调函数,这样有利于跨平台;
下面主要看看这两种调用约定的区别:
#include <IOSTREAM>
using namespace std;
int _cdecl Add(int a,int b)
{
return a+b;
}
int WINAPI Reduce(int a,int b)
{
return a-b;
}
void main()
{
int a=10 ,b=10;
int result;
result=Add(a,b);
result=Reduce(a,b);
}
15: result=Add(a,b);
004010C6 mov eax,dword ptr [ebp-8]
004010C9 push eax //入栈
004010CA mov ecx,dword ptr [ebp-4]
004010CD push ecx //入栈
004010CE call @ILT+0(Add) (00401005)
004010D3 add esp,8 //清除栈
004010D6 mov dword ptr [ebp-0Ch],eax
16: result=Reduce(a,b);
004010D9 mov edx,dword ptr [ebp-8]
004010DC push edx//入栈
004010DD mov eax,dword ptr [ebp-4]
004010E0 push eax//入栈
004010E1 call @ILT+20(Reduce) (00401019)
004010E6 mov dword ptr [ebp-0Ch],eax
4: {
00401040 push ebp
00401041 mov ebp,esp
00401043 sub esp,40h
00401046 push ebx
00401047 push esi
00401048 push edi
00401049 lea edi,[ebp-40h]
0040104C mov ecx,10h
00401051 mov eax,0CCCCCCCCh
00401056 rep stos dword ptr [edi]
5: return a+b;
00401058 mov eax,dword ptr [ebp+8]
0040105B add eax,dword ptr [ebp+0Ch]
6: }
0040105E pop edi
0040105F pop esi
00401060 pop ebx
00401061 mov esp,ebp
00401063 pop ebp
00401064 ret//恢复指令地址
8: {
00401070 push ebp
00401071 mov ebp,esp
00401073 sub esp,40h
00401076 push ebx
00401077 push esi
00401078 push edi
00401079 lea edi,[ebp-40h]
0040107C mov ecx,10h
00401081 mov eax,0CCCCCCCCh
00401086 rep stos dword ptr [edi]
9: return a-b;
00401088 mov eax,dword ptr [ebp+8]
0040108B sub eax,dword ptr [ebp+0Ch]
10: }
0040108E pop edi
0040108F pop esi
00401090 pop ebx
00401091 mov esp,ebp
00401093 pop ebp
00401094 ret 8 //恢复指令地址和清理栈
//主要是想巩固一下自己的记忆