我在许多函数之前看到了fastcall
符号。为什么使用它?
答案 0 :(得分:23)
该函数之前的符号称为“调用约定”。它指定编译器如何(在较低级别)将输入参数传递给函数并在执行后检索其结果。
有许多不同的通话约定,最受欢迎的是stdcall
和cdecl
。
您可能认为只有一种方法可以实现,但实际上,您可以通过多种方式调用函数并将变量传入和传出。您可以将输入参数放在堆栈上(push,push,push to call; pop,pop,pop来读取输入参数)。或者你可能宁愿把它们放在寄存器中(这是fastcall
- 它试图在寄存器中加入一些输入参数以获得速度)。
但那顺序怎么样?你是从左到右还是从右到左推动它们?结果如何 - 总是只有一个(假设没有参考参数),那么你将结果放在堆栈中,寄存器中,某个存储器地址吗?
另外,让我们假设你正在使用堆栈进行通信 - 在调用函数之后实际清除堆栈的工作是谁 - 调用者还是被调用者?
备份然后恢复(某些)CPU寄存器的内容怎么样 - 如果调用者这样做,或者被调用者是否保证它将按原样返回所有内容?
最流行的调用约定(到目前为止)是cdecl
,它是C和C ++中的标准调用约定。 WIN32 API使用stdcall
,这意味着调用WIN32 API的任何代码都需要使用stdcall
进行这些函数调用(使其成为另一种流行的选择)。
fastcall
有点奇怪 - 人们意识到许多函数只有一个输入/输出参数,从基于内存的堆栈推送和弹出是相当多的开销,并使函数调用有点因此,不同的编译器引入了(不同的)调用约定,这些约定将一个或多个参数放在寄存器中,然后将其余的放入堆栈中以获得更好的性能。问题是,并非所有编译器都使用相同的规则来处理fastcall
的位置和使用者,因此您在使用它时必须小心,因为您永远不会知道谁做了什么。最后,有关fastcall
效果优势的信息,请参阅Is fastcall really faster?。
复杂的东西。
要记住一些重要事项:如果您不知道完全您正在做什么,请不要添加或更改调用约定,因为如果调用者和被调用者都不同意调用约定,你最终可能会遇到堆栈损坏和段错误。当您在DLL /共享库中调用函数并且编写的程序依赖于DLL / SO / dylib是某种调用约定(例如,cdecl
)时,通常会发生这种情况,然后重新编译库使用不同的调用约定(例如,fastcall
)。现在,旧程序无法再与新库通信。
答案 1 :(得分:2)
标题为fastcall的约定尚未标准化,并且已根据编译器供应商的不同实现。通常,fastcall调用约定会在寄存器中传递一个或多个参数,从而减少调用所需的内存访问次数。