1、函数调用约定一般规定如下三个方面:
1)函数参数的传递顺序和方式;
2)栈的维护;
3)名字修饰的策略。
2、常见的函数调用约定方式:
调用约定 | 出栈方式 | 参数传递 | 名字修饰 |
cdecl | 函数调用方 | 从右至左的顺序压参数入栈 | 下划线+函数名 |
stdcall | 函数本身 | 从右至左的顺序压参数入栈 | 下划线+函数名+@+参数的字节数,如函数 int func(int a, double b)的修饰名是_func@12 |
fastcall | 函数本身 | 头两个(4字节)类型或占更少字节的参数被放入寄存器,其它剩下的参数按从右至左的顺序压参数入栈 | @+函数名+@+参数的字节数 |
pascal | 函数本身 | 从左至右的顺序压参数入栈 | 较为复杂,参见pascal文档 |
3、各个调用约定在vs2017下反汇编情况:
1 #include<iostream> 2 3 using namespace std; 4 5 int _cdecl f1(int a, int b, long long c) { return a + b + c; } 6 int _stdcall f2(int a, int b, long long c){ return a + b + c; } 7 int _fastcall f3(int a, int b, long long c){ return a + b + c; } 8 9 int main() 10 { 11 int a = 10; 12 int b = 20; 13 long long c = 4; 14 15 f1(a, b, c); 16 f2(a, b, c); 17 f3(a, b, c); 18 return 0; 19 }
反汇编如下:
1 int a = 10; 2 003623E8 mov dword ptr [a],0Ah 3 int b = 20; 4 003623EF mov dword ptr [b],14h 5 long long c = 4; 6 003623F6 mov dword ptr [c],4 7 003623FD mov dword ptr [ebp-20h],0 8 9 f1(a, b, c); 10 00362404 mov eax,dword ptr [ebp-20h] 11 00362407 push eax 12 00362408 mov ecx,dword ptr [c] 13 0036240B push ecx 14 0036240C mov edx,dword ptr [b] 15 0036240F push edx 16 00362410 mov eax,dword ptr [a] 17 00362413 push eax 18 00362414 call f1 (03616A9h) 19 00362419 add esp,10h 20 f2(a, b, c); 21 0036241C mov eax,dword ptr [ebp-20h] 22 0036241F push eax 23 00362420 mov ecx,dword ptr [c] 24 00362423 push ecx 25 00362424 mov edx,dword ptr [b] 26 00362427 push edx 27 00362428 mov eax,dword ptr [a] 28 0036242B push eax 29 0036242C call f2 (03616A4h) 30 f3(a, b, c); 31 00362431 mov eax,dword ptr [ebp-20h] 32 00362434 push eax 33 00362435 mov ecx,dword ptr [c] 34 00362438 push ecx 35 00362439 mov edx,dword ptr [b] 36 0036243C mov ecx,dword ptr [a] 37 0036243F call f3 (03616AEh)
参考:《程序员的自我修养--链接、装载与库》