例如,
#include <stdio.h>
void foo();
int main(void)
{
foo();
foo(42);
foo("a string", 'C', 1.0);
return 0;
}
void foo()
{
puts("foo() is called");
}
输出:
foo() is called
foo() is called
foo() is called
此代码编译良好(没有使用 clang 的警告)并且运行良好。但我想知道传递给 foo()
的值会发生什么?它们是被插入堆栈还是被丢弃?
也许这个问题听起来没什么用,但确实有道理。例如,当我有 int main()
, 而不是 int main(void)
, 并将一些命令行参数传递给它,main()
的行为将会如何?会受影响吗?
此外,当使用 <stdarg.h>
时, 在 ...
之前至少需要一个命名参数通过 ISO C。我们是否可以使用 void foo()
这样的声明?将从零到无限的参数传递给函数?
我注意到 void foo()
是一个“非原型(prototype)声明”并且 void foo(void)
刚才是一个“原型(prototype)声明”。这有点相关吗?
澄清
似乎这个问题被标记为重复What does an empty parameter list mean? [duplicate] (有趣的是,这个问题也是重复的......)。事实上,我不认为我的问题与那个问题有任何关系。它着重于“void foo()
在 C 中的含义”,但我知道这意味着“我可以向它传递任意数量的参数”,而且我也知道这是一个过时的功能。
但是这个问题是很不一样的。关键词是“如果”。我只想知道我是否将不同数量的参数传递给 void foo()
, 就像上面的示例代码一样,它们可以在 foo()
内部使用吗? ?如果是这样,这是怎么做到的?如果不是,传递的参数有什么区别吗?这是我的问题。
最佳答案
正如 Jonathan Leffler 所说,C 的调用约定确定调用函数(而不是被调用函数)负责从堆栈中弹出参数,因此即使参数与被调用函数的预期不匹配,程序也不会崩溃。
我要补充一点,C 的调用约定还确定参数以相反的顺序被压入堆栈(即调用 foo (1, 2)
压入 2
,然后压入 1
)。这允许被调用函数访问第一个参数,即使它不知道其余参数。例如,函数声明为 int foo (int a, int b, ...)
将能够访问a
和 b
即使不知道传递了哪些其他参数:a
和 b
就在堆栈顶部。访问其他参数需要堆栈指针 hack,这正是 printf
做。当然,通过使用与预期不同的参数(例如,printf ("%d%d", 3.5);
),可以很容易地得到有趣的结果。
所以,根据问题,是的,int foo ()
可以使用任何数量/类型的参数安全地调用,在 1980 年代,在堆栈上使用指针 hack 来访问未知参数将被视为“正常做法”。当可移植性和可读性变得越来越受关注时,<stdarg.h>
作为实现这些指针 hack 的可移植方式出现(编译器将为目标平台生成正确的代码,因此它不再是“hack”)。然而,正如所说,<stdarg.h>
至少需要一个参数,所以它不能帮助 int foo ()
.
关于c - 如果我声明一个参数表为空的函数,然后将参数传递给它会怎样?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35426282/