函数调用约定主要约束了两件事:
1.参数传递顺序
2.调用堆栈由谁(调用函数或被调用函数)清理
常见的函数调用约定:stdcall cdecl fastcall thiscall naked call
调用方 | 推入堆栈的参数,按相反的顺序 (从右向左) | |
无 | 按顺序加载在 CLR 表达式堆栈上的参数 (从左到右)。 | |
被调用方 | 推入堆栈的参数,按相反的顺序 (从右向左) | |
被调用方 | 存储在注册,然后被推入堆栈 | |
被调用方 | 推入堆栈;在 ECX 存储的 这 指针 |
着重讲一下stdcall cdecl fastcall,
stdcall 与cdecl有相同的参数传递顺序 ,参数从右向左压入堆栈,不同的是清理堆栈工作由谁完成。stdcall由调用的子函数完成,cdecl由调用的函数完成。
VC中 C/C++ 默认调用约定是cdecl.
上代码,如下。
#include <stdio.h>
//__fastcall 调用
int __fastcall sum_fas(int a, int b, int c)
{
return (a + b + c);
}
//__stdcall 调用
int __stdcall sum_std(int a, int b, int c)
{
return (a + b + c);
}
//__cdecl 调用
int __cdecl sum_cde(int a, int b, int c)
{
return (a + b + c);
}
//VC中 C/C++ 默认调用
int sum (int a, int b, int c)
{
return (a + b + c);
}
int main()
{
int c1,c2,c3,c4;
c1 = sum_fas(2, 3, 5);
c2 = sum_std(2, 3, 5);
c3 = sum_cde(2, 3, 5);
c4 = sum(2,3,5);
return 0;
}
汇编代码:
int main()
{
011D1500 push ebp
011D1501 mov ebp,esp
011D1503 sub esp,0F0h
011D1509 push ebx
011D150A push esi
011D150B push edi
011D150C lea edi,[ebp-0F0h]
011D1512 mov ecx,3Ch
011D1517 mov eax,0CCCCCCCCh
011D151C rep stos dword ptr es:[edi]
int c1,c2,c3,c4;
c1 = sum_fas(2, 3, 5);
011D151E push 5
011D1520 mov edx,3
011D1525 mov ecx,2
011D152A call sum_fas (011D1037h)
011D152F mov dword ptr [c1],eax
c2 = sum_std(2, 3, 5);
011D1532 push 5
011D1534 push 3
011D1536 push 2
011D1538 call sum_std (011D10E6h)
011D153D mov dword ptr [c2],eax
c3 = sum_cde(2, 3, 5);
011D1540 push 5
011D1542 push 3
011D1544 push 2
011D1546 call sum_cde (011D10B4h)
011D154B add esp,0Ch
011D154E mov dword ptr [c3],eax
c4 = sum(2,3,5);
011D1551 push 5
011D1553 push 3
011D1555 push 2
011D1557 call sum (011D1113h)
011D155C add esp,0Ch
011D155F mov dword ptr [c4],eax
return 0;
011D1562 xor eax,eax
}
参考:http://technet.microsoft.com/zh-cn/interopmigration/984x0h58