传递超过所需参数给C函数

3

如果我向一个函数传递比所需参数更多的参数会发生什么?我原本期望被调用的函数中会出现一些错误,但在一些小的测试代码中一切都运行正常。

例如:

void print()
{
    int x=10;
    printf("%d\n",x);
}
void main()
{
    print(0,0,0,0,0);
}

也许这可以回答你的问题:https://dev59.com/FnVD5IYBdhLWcg3wNY1Z - SidR
4
作为惯例,在C语言中,永远不要使用()声明或定义函数。应该始终使用(void)来表示空参数列表。这样你的好编译器会立刻提示你犯了错误。 - Jens Gustedt
1
此外,C11 6.11“未来语言方向”将其标记为过时行为,可能在C标准的未来版本中无法编译。 “使用带有空括号的函数声明符(而不是原型格式参数类型声明符)是一种过时的特性。” - Lundin
@JensGustedt 啊!好的观点。但是,当我使用库时,这个观点就不适用了。基本上,如果print作为一个.o而没有头文件提供,那么我仍然可以调用带有很多参数的print并将其链接。 - vpillai
根据下面的答案,我理解在C规范中它是未定义的。但是,我仍然很想知道在GCC中具体会发生什么。 - vpillai
1
@vpillai,任何给定架构上发生的具体情况取决于该架构使用的调用约定。有些架构例如将所有参数放在堆栈中,而其他一些架构则可以将第一个参数(如果类型适当)传递到硬件寄存器中。在这两种情况下,您可能会遇到麻烦,因为一端可能访问另一端不希望访问的内容。还要注意,如果没有原型,则所有“狭窄”类型的参数都会扩展为int,因此可能会发生非常奇怪的事情。 - Jens Gustedt
3个回答

5

这是一种未定义行为。

(C99,6.5.2.2p6)“如果表示调用函数的表达式具有不包括原型的类型,则……如果参数数量不等于参数数量,则行为未定义。”

我们知道从6.9.1p7,print函数没有提供原型。

C99, 6.9.1p7) “如果声明符包括参数类型列表,则该列表还指定所有参数的类型;这样的声明符也可以作为后来在同一翻译单元中对同一函数进行调用的函数原型。 如果声明符包括标识符列表,142)则参数的类型应在随后的声明列表中声明。”

由于不存在约束违规,因此不需要诊断消息。


好的。但是,有没有想法我传递的所有零会放在哪里?(也许,一个特定于GCC的答案) - vpillai
4
@vpillai undefined 是未定义的,但使用 gcc 的汇编转储显示它们被推送到栈中。 - ouah
我的疑问实际上来自于这个问题。它不会覆盖print的本地变量吗? - vpillai
@vpillai,再次提醒您,未定义是未定义的,您不应该这样做,否则可能会覆盖任何内容。 - ouah
2
@ouah参数的位置是与ABI有关的。在我的x86_64上,它们是通过寄存器传递的。 - Daniel Fischer
@DanielFischer 是的,感谢您的精确说明。我应该提到我的实现中的“汇编转储”。 - ouah

2
在函数调用时,编译器会决定将参数放入CPU寄存器中(如果它们适合的话),否则参数将进入堆栈内存(http://www.technochakra.com/wp-content/uploads/assembly_stack.jpg)。
当你添加不存在的参数时,可能会导致未定义的行为,因为被调用的函数代码可能未对齐堆栈内存访问。换句话说,调用代码将以不同于函数内部预期布局的布局写入堆栈。

-2
会遇到编译错误。 我在 Visual C++ 6.0 中编译代码,输出为:
error C2660: 'print' : function does not take 5 parameters

但在GCC中,只有当我有函数原型时才会出现编译错误。 - vpillai
1
这是不相关的。首先:您正在使用C++编译器进行编译。其次:该编译器以其非常差的C标准兼容性而闻名。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接