12

我已经看到fastcall在许多函数之前附加的符号。为什么使用它?

4

2 回答 2

26

该函数之前的符号称为“调用约定”。它指定编译器如何(在低级别)将输入参数传递给函数并在执行后检索其结果。

有许多不同的调用约定,最流行的是stdcallcdecl

您可能认为只有一种方法可以做到这一点,但实际上,您可以通过多种方法调用函数并传入和传出变量。您可以将输入参数放在堆栈上(推送、推送、推送调用;弹出、弹出、弹出读取输入参数)。或者您可能更愿意将它们放在寄存器中(这是fastcall- 它试图将一些输入参数放入寄存器中以提高速度)。

但那么订单呢?你是从左到右还是从右到左推动它们?结果怎么样 - 总是只有一个(假设没有引用参数),所以你是否将结果放在堆栈中,在寄存器中,在某个内存地址?

另外,假设您正在使用堆栈进行通信-调用函数后实际清除堆栈的工作是谁-调用者还是被调用者?

备份然后恢复(某些)CPU 寄存器的内容怎么样 - 调用者应该这样做,还是被调用者保证它将按原样返回所有内容?

最流行的调用约定(到目前为止)是cdecl,它是 C 和 C++ 中的标准调用约定。WIN32 API 使用stdcall,这意味着调用 WIN32 API 的任何代码都需要stdcall用于这些函数调用(使其成为另一个流行的选择)。

fastcall有点奇怪——人们意识到许多函数只有一个 in/out 参数,从基于内存的堆栈中推入和弹出是相当多的开销,并且使函数调用有点繁重,因此引入了不同的编译器(不同) 调用约定,将一个或多个参数放在寄存器中,然后将其余参数放在堆栈中以获得更好的性能。问题是,并不是所有的编译器都使用相同的规则来定义什么去哪里和谁做什么fastcall,因此你在使用它时必须小心,因为你永远不知道谁在做什么。最后,看看fastcall 真的更快吗?有关fastcall性能优势的信息。

复杂的东西。

需要记住的重要一点:如果您不确切知道自己在做什么,请不要添加或更改调用约定,因为如果调用者和被调用者都不同意调用约定,您可能会最终堆栈损坏和段错误。这通常发生在当您在 DLL/共享库中调用函数并且编写的程序依赖于 DLL/SO/dylib 作为某个调用约定(例如cdecl)时,然后使用不同的调用约定重新编译该库(比如说,fastcall)。现在旧程序无法再与新库通信。

于 2012-05-20T06:33:36.627 回答
2

维基百科指出

名为 fastcall 的约定尚未标准化,并且根据编译器供应商的不同,实现方式也有所不同。通常,fastcall 调用约定在寄存器中传递一个或多个参数,从而减少调用所需的内存访问次数。

于 2012-05-20T06:31:33.063 回答