参数比预期更多的函数调用

Function call with more parameters than expected

我在审查一些代码时遇到了类似的情况。

文件foo.c:

int bar(int param1)
{
    return param1*param1;
}

文件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));
}

运行 gcc main.c foo.c -Wall --pedantic -O0 它编译、链接并正常工作,而不会在此过程中抛出任何警告。这是为什么?

谢谢!

这实际上取决于调用约定和体系结构。例如,对于 x86 上的 cdecl,其中参数从右向左推送并且调用者恢复堆栈,附加参数的存在对函数 bar:

是透明的
push    11
push    10
call    _bar
add     esp, 8

bar 将仅 "see" 10,并且将按预期使用该参数 returning 100。堆栈随后恢复,因此 main 中也没有错位;如果您刚刚通过了 10,它会在 esp 中加上 4。

这两个 MSVC on Windows and the System V ABI 的 x64 调用约定也是如此,其中前几个 1 个整数参数在寄存器中传递;第二个参数将通过 main 中的调用填充到其指定的寄存器中,但 bar.

甚至不会查看

但是,如果您尝试使用被调用方负责清理堆栈的备用调用约定,您将 运行 在构建阶段或(更糟)在 运行时间。 stdcall,例如,用参数列表使用的字节数修饰函数名称,所以我什至无法通过将 bar 更改为使用 link 最终的可执行文件 stdcall 改为:

error LNK2019: unresolved external symbol _bar@8 referenced in function _main

这是因为 bar 现在在其对象文件中有签名 _bar@4,这是应该的。

如果您使用过时的调用约定 pascal,这会变得很有趣,其中参数从左到右推送:

push 10
push 11
call _bar

现在 bar return 是 121,而不是 100,正如您预期的那样。也就是说,如果函数成功 returns,它不会,因为被调用者应该清理堆栈但由于额外参数而失败,破坏了 return 地址。

1:Windows 上的 MSVC 为 4;系统 V ABI 上的 6

通常你会有这样的文件结构:

foo.c

#include "foo.h"

int bar(int param1)
{
    return param1*param1;
}

foo.h

int bar(int param1);

main.c

#include <stdio.h>
#include "foo.h"

int main (void)
{
    int param = 2, unused = 0;
    printf("%d\n", bar(param, unused));
}

现在,只要您使用 bar 和不匹配的参数,就会出现编译错误。