9

例如,如果我用一个空参数表声明一个函数,然后将参数传递给它呢?

#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 

此代码编译井(不使用铛警告)和运行良好。但我想知道传递给foo()的值会发生什么?他们是被推入堆栈还是被丢弃?

也许这个问题听起来毫无用处,但它确实有道理。例如,当我有int main()而不是int main(void),并将一些命令行参数传递给它时,main()的行为是否会受到影响?

此外,使用<stdarg.h>时,至少有一个名为参数...之前ISO C要求的是有可能,我们可以利用这种声明的void foo()的从零传递到无限的参数的函数?

我注意到void foo()是一个“非原型声明”,而void foo(void)是刚才的“原型声明”。这有点相关吗?


澄清

看来,这个问题被标记复制到What does an empty parameter list mean? [duplicate](有趣的是,这个问题也被复制......)。事实上,我认为我的问题与这个问题没有任何关系。它着重于“什么void foo()在C中的含义”,但我知道这意味着“我可以传递任何数量的参数”,并且我也知道这是一个过时的功能。

但这个问题是完全不同的。关键字是“如果”。我只想知道,如果我通过了void foo()的不同数量的参数,就像上面的示例代码一样,它们可以在foo()之内使用吗?如果是这样,这是如何完成的?如果不是,所通过的论点是否有所作为?这是我的问题。

+0

请注意,不能使用无参数函数来传递零个或多个可变参数,因为'va_start()'宏需要最后一个已命名参数的名称 - 并且如果没有命名参数,则它无法工作。 –

+0

你说得对,''至少需要一个参数。这就是为什么我想知道是否可以使用'()'。顺便说一句:我澄清了这个问题,你可以看看它吗? –

+2

将值压入堆栈;当函数返回时,堆栈被调用函数清除(这是C调用约定;其他语言使用不同的约定,因为被调用的函数知道它传递了多少个参数或者使用了多少参数空间来传递它们的参数,以及可以清除堆栈)。没有可移植的方式来使用传递给函数的参数,如'foo()'。 –

回答

2

在C中,void foo()声明了一个函数,该函数接受了未指定数量的参数。甲function被以下面的方式宣称:

return-type function-name(parameter-list,...) { body... } 

parameter-list是,该函数接受由逗号分隔的参数列表。如果没有给出参数,那么该函数不会接受任何参数,而应该使用一组空括号或关键字void来定义。如果参数列表中的变量前没有变量类型,则假定为int。

1

正如Jonathan Leffler所说,C的调用约定确定调用函数(而不是被调用的函数)负责从栈中弹出参数,所以即使参数与被调用函数期望的不匹配,程序也不会崩溃。

我会补充一点,C的调用约定也规定参数以相反的顺序压入堆栈(即调用foo (1, 2)推入2然后1)。这允许被调用函数访问第一个参数,即使它不知道其余参数。例如,一个声明为int foo (int a, int b, ...)的函数将能够访问ab,即使不知道其他参数是否已经通过:ab只是在栈顶。访问其他参数需要堆栈指针黑客,这正是printf所做的。当然,通过使用不同的参数(例如,printf ("%d%d", 3.5);),可以轻松获得有趣的结果。

因此,按照这个问题,是的,int foo()可以用任意数量/类型的参数安全地调用,并且在20世纪80年代,在栈上使用指针黑客来访问未知参数被认为是“正常实践”。当可移植性和可读性越来越受到关注时,<stdarg.h>作为实现这些指针黑客的便携式方式出现了(编译器将为目标平台生成正确的代码,因此它不再是“黑客”)。但是,正如所说的,<stdarg.h>至少需要一个参数,所以它不能帮助int foo()

相关问题