本文链接:C语言的三种函数调用方式
C语言提供了三种调用函数的方式:cdecl,stdcall和fastcall。cdecl是标准的C语言调用函数的方式(C declared);stdcall是大多数链接库中使用的调用方式,比如Windows API,JNI API等等;fastcall顾名思义,就是说它调用起来比较快。这三种方式在C语言编写中几乎没有任何差别,但对应了不同的底层实现。
要使用这三种调用函数的方法,需要在函数声明和定义时写明__cdecl(可不写),__stdcall,__fastcall。注意每个词前面是两个下划线符号。
下面来一个简单的例子:
cdecl的调用方式是由调用方控制参数栈,被调用的函数在返回时不需要对自己使用的栈进行清理,所以cdecl_func编译出来的汇编代码如下:
调用方的代码如下:
可以看出其采用了放置参数到栈上,然后调用的方式,可以避免清理栈的问题。
stdcall和cdecl不同,是由被调用函数来清理参数栈的,所以编译出来的函数最后一句不同,表示返回的同时清理12字节的栈空间:
同时,调用时采用压栈的方式:
这里有一点,stdcall的函数在gcc编译器下生成的标识符名会加“@参数字节数”,有时候会在(动态)链接时引起“找不到标识符”错误。通过加入-Wl,--kill-at
可以移除“@”,从而修复错误,在此不作详述。
fastcall通过使用寄存器传参,达到快速调用的目的,使用的寄存器为ecx和edx,如果有多于两个的参数,则使用stdcall的方式进行栈传参:
gcc下fastcall函数的命名方式更加不同,生成的标识符是以“@”开头,而不是下划线“_”。
至此,就介绍完毕C语言中三种不同的调用方式。在汇编编程的时候,最常用的往往是stdcall,而且大部分API库也都用了这种方式。但是cdecl有其独特的优势,就是可以支持不定长的参数,比如printf。在gcc中,如果给一个stdcall函数设置不定长参数,那么编译后的结果就会失去stdcall的属性,实际上成为一个cdecl函数。至于fastcall,可以在一些需要快速传参的地方使用,但是很多时候还是使用内联函数更好一些。