3. __fastcall
在VC中这种调用约定和前两种比较起来,使用的比较少。(Borland C++的默认调用约定就是这个,但是和VC的实现有点不同。)还是让我们先看看编译器的工作。
// 调用函数f3 f3(1, 2, 3, 4); // 函数f3的实现 int __fastcall f3(int a, int b, int c, int d) { return a + b + c + d; }
;调用函数f3,4个参数分别是1,2,3和4,前两个参数放在ecx和edx寄存器中,后两个压栈
push 4 ;参数从右到左开始压栈,先压第4个参数
push 3 ;第3个参数压栈
mov edx,2 ;第2个参数放在edx寄存器中
mov ecx,1 ;第1个参数放在ecx寄存器中
call f3 (401014h) ;调用函数
;函数f3的实现
push ebp ;和f1,f2一样
mov ebp,esp ;和f1,f2一样
sub esp,8 ;在栈上空出8个byte的空间,用来存放两个int的临时变量
mov dword ptr [ebp-8],edx ;把第2个参数(edx)放到第2个变量
mov dword ptr [ebp-4],ecx ;把第1个参数(ecx)放到第1个变量
mov eax,dword ptr [ebp-4] ;eax = 第1个变量(第1个参数)
add eax,dword ptr [ebp-8] ;eax = eax + 第2个变量(第2个参数)
add eax,dword ptr [ebp+8] ;eax = eax + 参数c
add eax,dword ptr [ebp+0Ch] ;eax = eax + 参数d
mov esp,ebp ;清除临时变量
pop ebp ;和f1,f2一样
ret 8 ;函数返回,返回值是eax,并清除栈上的参数
从上面最后一行反汇编"ret 8"可以看到,__fastcall和__stdcall一样,也是函数本身来清除栈上的参数,这也就意味着__fastcall也有__stdcall的缺点:不支持可变参数个数的函数。
和__stdcall不同的是,__fastcall把第一,第二个参数放到了寄存器中,而不是压栈,因为寄存器的读写速度比栈快很多,这也就是为什么它叫快速调用(fast call。注意:在VC中的某些情况下,__fastcall比__stdcall和__cdecl慢)。
再介绍其他调用约定之前,让我们先回顾一下__cdecl,__stdcall和__fastcall。并且补充一些它们之前的区别(这些区别不太重要,所以上面没有讨论,只在这里列出)
|
|
__cdecl
|
__stdcall
|
__fastcall
|
|
压栈顺序
|
从右到左
|
从右到左
|
从右到左,前两个参数放在ecx, edx
|
|
谁清除栈上参数
|
调用者(caller)
|
函数(被调用者callee)
|
函数(被调用者callee)
|
|
默认调用约定的编译器参数
|
/Gd
|
/Gz
|
/Gr
|
|
可变参数个数的函数
|
支持
|
不支持
|
不支持
|
|
C的函数名修饰规范Name-decoration convention
|
加下划线前缀,如:_func
|
下划线开头,函数名,然后@符号,最后是参数的总byte数。如:int f(int a, double b ),名字为_f@12
|
以@开头,其他和__stdcall一样。如:@f@12
|