Kai*_*Kai 6 c parameter-passing calling-convention
我正在审查一些代码,我发现了类似的东西.
文件foo.c:
int bar(int param1)
{
return param1*param1;
}
Run Code Online (Sandbox Code Playgroud)
文件main.c:
#include <stdio.h>
int bar(int param1, int unusedParam);
int main (void)
{
int param = 2, unused = 0;
printf("%d\n", bar(param, unused));
}
Run Code Online (Sandbox Code Playgroud)
运行gcc main.c foo.c -Wall --pedantic -O0它编译,链接和正常工作,而不会在过程中抛出一个警告.这是为什么?
谢谢!
这实际上取决于调用约定和体系结构.例如,cdecl在x86上,参数从右向左推,调用者恢复堆栈,附加参数的存在对函数是透明的bar:
push 11
push 10
call _bar
add esp, 8
Run Code Online (Sandbox Code Playgroud)
bar只会"看到" 10,并将按预期与该参数一起运行,返回100.之后堆栈恢复,因此main两者都没有错位; 如果你刚刚通过10它,它将添加4来esp代替.
对于Windows上的MSVC和System V ABI的x64调用约定也是如此,其中前几个1个整数参数在寄存器中传递; 第二个参数将通过调用填入其指定的寄存器中main,但是甚至没有查看bar.
但是,如果您尝试使用备用调用约定,其中被调用者负责清理堆栈,则在构建阶段或运行时(更糟糕)会遇到麻烦.stdcall例如,用参数列表使用的字节数装饰函数名,所以我甚至无法通过bar改为使用来链接最终的可执行文件stdcall:
error LNK2019: unresolved external symbol _bar@8 referenced in function _main
Run Code Online (Sandbox Code Playgroud)
这是因为它bar现在_bar@4在其目标文件中具有签名,就像它应该的那样.
如果您使用过时的调用约定pascal,其中参数从左向右推送,这会很有趣:
push 10
push 11
call _bar
Run Code Online (Sandbox Code Playgroud)
现在bar返回121,而不是100,就像你预期的那样.也就是说,如果函数成功返回,那么它将不会,因为被调用者应该清理堆栈但由于额外的参数而失败,因此废弃了返回地址.
1:4用于Windows上的MSVC; 6在System V ABI上