java函数调用约定_函数调用约定 (cdecl stdcall)

本文介绍了C/C++中的函数调用约定,包括cdecl和stdcall的参数入栈顺序、堆栈清理责任以及函数名修饰规则。cdecl是默认约定,参数从右至左入栈,调用方清理堆栈;stdcall参数同样右至左入栈,但由被调函数清理。此外,还提及了fastcall和__thiscall,并讨论了函数名修饰在C和C++中的不同。
摘要由CSDN通过智能技术生成

函数调用约定 (cdecl stdcall)

在 C 语言里,我们通过阅读函数声明,就知道怎么携带参数去调用函数,也能在函数体定义内使用这些参数。但是 CPU 并不直接完成函数调用的传参操作,这需要人为的约定。这些约定被编译器识别和使用,生成所需的代码。

一般每个线程都维护着一个堆栈,称为调用栈。调用方想进行函数调用并传递参数,就往栈里压入参数。被调函数从栈顶取出一定数量的参数使用,这就完成了参数的传递。这个过程需要一些约定,使调用方和被调函数都能正确地识别对应参数的位置等。

顺便一提,函数返回值是怎么实现的呢?是往调用栈里压入了一个空值占位,被调函数往这个位置写入返回值,调用方再取出来使用,这就完成了返回值的传递。

函数调用约定主要包括三方面的内容:

压栈时的参数顺序,是从左至右还是从右至左

调用完成后堆栈由谁清理,是调用方还是被调函数

编译器的函数名修饰约定

主要有 cdecl stdcall 两种不同的约定,同时这也是 C 的关键字。

cdecl

cdecl 表示:

参数从右至左入栈

调用方来维护堆栈

告知 C 编译器,输出的函数名应该前加 _,即修饰为 _

cdecl 是 C/C++ 的默认函数调用约定。

在使用时,用特定的关键字来修饰函数名,Windows 下常用的 Visual C++ Compiler 支持 __cdecl;Linux 下常用的 GCC 支持 __attribute__((cdecl))。

cdecl 带来了两件事:

被调函数虽然不知道有多少参数入了栈,但会自上而下按需取走需要的参数。你可能想到了,使用可变参数(vararg/stdarg)的函数(如 printf)就只能使用 cdecl 约定。

每次函数调用后都要生成一段清栈代码,生成的目标代码会比较大

stdcall

stdcall 又称为 Pascal 约定,因为这也是 Pascal 语言使用的函数调用约定。

stdcall 表示:

参数从右至左入栈

被调函数来维护堆栈。这一过程一般通过指令 retn x 完成,x 是参数占用的字节数(记为 )

告知 C 编译器,输出的函数名应该前加 _ 后跟 @,即修饰为 _@

这里的第三点,举例说明:在 win64 环境下,函数 int __stdcall foo(void * p) 就可以在外部使用 _foo@8 来引用。

在使用时,vc++ 下使用 __stdcall;gcc 下使用 __attribute__((stdcall))。

在 Windows 编程中,宏 CALLBACK WINAPI 都指向 __stdcall。

因为 stdcall 严格控制了参数的字节数,所以不能实现可变参数。

__fastcall __thiscall

在 __stdcall 之上,引入了两个优化版本 __fastcall __thiscall。

__fastcall 将前两个或多个参数由寄存器传递(由编译器选决定选用哪些寄存器),以优化大多数参数个数较少的函数的调用耗时。C 编译器输出的函数名修饰为 @@。

__thiscall 是 C++ 中引入的,这不是关键字,不可手动指定,而是针对类成员函数自动添加,将 this 指针放在特定寄存器中(由编译器自行决定)。

__fastcall 和 __thiscall 都与编译器相关,可移植性较差。

naked call

表示函数无需保护现场(prolog)和恢复现场(epilog)的代码。

这在 vc++ 下不是通过关键字实现的,而是通过 __declspec(naked) 函数声明;gcc 下通过 __attribute__((naked))。

naked 标志跟 __inline 内联函数是不同的机制,不能一起使用。

关于函数名修饰规则

编译器输出的函数名,在其他目标文件中引用该函数时使用。

C 语言采用上述规则,而 C++ 则采用另一套规则,该规则详尽描述了函数原型,因此显得有些复杂。

但是,在 C++ 中可以使用 extern "C" 来要求以 C 编译器的方式来编译和链接,这也会以 C 的方式来修饰函数名。

在编写可移植的代码时,常用以下代码段来实现这一目标:

#ifdef __cplusplus

extern "C" {

#endif

// code

#ifdef __cplusplus

}

#endif /* end of __cplusplus */

拓展阅读

转载至链接:https://my.oschina.net/tridays/blog/1930040

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值