C语言的三种函数调用方式

C语言的三种函数调用方式

本站内容版权属于本人。转载须告知本人,写明出处,并在文首提供指向本站对应文章的链接。
本文链接: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,可以在一些需要快速传参的地方使用,但是很多时候还是使用内联函数更好一些。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

*

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据