在C语言中,int foo (int argc, ...)、int foo()和int foo(void)有什么区别?

5
今天我第一次意识到 int foo()int foo(void) 是不同的,前者允许 任意数量 的输入,而后者只允许 零个int foo() 是否会忽略所有给定的输入?如果是这样,那么允许这种形式的函数有什么意义?如果不是这样,那么如何访问它们,这与具有变量参数列表的情况有何不同(例如像 int foo (int argc, ...) 这样)?

4个回答

4
理解这一点的关键是理解调用栈。在C语言中调用函数时(使用标准的x86 ABI - 其他平台可能会有所不同),调用者会将所有参数以相反的顺序推入堆栈中,然后才会调用被调用者。然后,被调用者可以读取它想要的任意数量的这些参数。如果使用foo(void),显然您将不会读取任何参数。如果使用foo(int),则可以将一个单词读入当前帧下面的堆栈中。
使用foo()(未指定参数)意味着编译器不会关心您传递给foo的参数。任何东西都可以;没有合同。如果foo有两个不同的实现,需要采用不同类型的参数,并且我想能够以编程方式切换它们,则这可能很有用。(在汇编编写函数时有时也很有用,因为您可以自己处理堆栈和参数。)但请记住,编译器不会为您执行任何类型检查,因此您必须非常小心。
这与foo(int,...)不同,因为在这种情况下,函数实际上有一种访问所有参数的方法(使用varargs)。从来没有理由实际上定义一个foo()函数,因为它基本上等同于foo(void)。foo()仅对声明有用。

我要在第一段中补充的是,调用者然后弹出(嗯...增加esp)堆栈中的参数,而不是被调用者。这很重要,因为它在被调用者清理时无法工作。 - Matt

1

首先,看一下 int foo()。这个形式是一个函数声明,它不提供一个原型——也就是说,它没有指定它的参数数量和类型。这是一种古老的函数声明形式——在 ANSI C 之前的 C 语言中,函数就是这样声明的。然而,这样的函数确实有固定数量和类型的参数——只是声明没有告诉编译器它们是什么,所以你需要在调用函数时自己确保正确。你可以像这样声明带参数的函数:

int foo(a, b)
    int a;
    char *b;
{
    return a + strlen(b);
}

这种函数声明形式现在已经过时。

int foo(void) 是一种现代声明形式,它提供了原型,它声明了一个不带参数的函数。int foo(int argc, ...) 也提供了原型;它声明了一个有一个固定整数参数和一个可变参数列表的函数。


0
在标准的C语言中,如果函数不接受任何参数,你必须使用int foo(void)
我猜当你传递空括号的参数给一个函数时,编译器会有所不同。但我认为没有办法访问这些参数。
至于main函数,唯一符合标准(纯C)的写法是int main(void)int main(int argc, char **argv)(或者是相同的char *argv[])。

从标准中查询后,似乎空括号也可以声明一个没有参数的函数。通常最好的做法是写上(void)来明确表明你不希望有任何参数。如果你有类似这样的进一步问题,阅读标准和你所使用的实现文档(如gcc、glibc)总是一个很好的主意。 你可以在网上免费找到标准的草案。 - Benjamin Maurer

0

在C++中,这两种形式是等效的,它们都声明了一个不带参数的函数。如果您尝试调用该函数并传入参数,则编译器将会报错。

另一方面,C和Objective-C都以不同的方式处理这两种形式。在这些语言中,第一种形式声明了一个带有未知数量参数的函数,而第二种形式则声明了一个根本不带参数的函数。因此,在C中,以下是有效的代码:

int foo() {
  return 5;
}
int main() {
  return foo(1, 2, 3);
}

编译器没有抱怨,代码运行良好(C++编译器会在相同的代码上报错)。

通常在C和Objective-C中,您想要使用第二种形式并包含显式的void来指示函数不应该带参数。然而,在C++中更常见的是使用第一种形式,因为它等效且更短。


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