为什么gcc允许将parameter passing给定义为无参数的函数?

我不明白为什么这个代码编译?

#include <stdio.h> void foo() { printf("Hello\n"); } int main() { const char *str = "bar"; foo(str); return 0; } 

gcc甚至不会抛出一个警告,我传递给foo()的参数太多。 这是预期的行为?

在C中,用一个空参数列表声明的函数在被调用时接受任意数量的参数,这些参数受普通算术升级的支配。 调用者的责任是确保提供的参数适合于函数的定义。

要声明一个采用零参数的函数,你需要写void foo(void);

这是历史原因; 最初,C函数没有原型,因为C是从无types语言B发展而来的。 当原型被添加时,原始的无types声明留在语言中以向后兼容。

要让gcc警告空参数列表,请使用-Wstrict-prototypes

如果没有指定参数types,则声明或定义函数时发出警告。 (如果前面有指定参数types的声明,则允许旧式函数定义不带警告。)

出于传统的原因,用参数列表声明一个函数本质上意味着“函数被调用时找出参数”。 要指定一个函数没有参数,请使用(void)

编辑:我觉得我正在这个老问题上的声誉。 只是让你的孩子知道什么编程曾经是这样的,这是我的第一个程序 。 (不是C;它显示了我们在此之前必须采取的措施。)

 void foo() { printf("Hello\n"); } foo(str); 

在C中,这段代码并没有违反约束条件(如果它是以void foo(void) {/*...*/} )的原型forms定义的,并且没有约束冲突,编译器就不会需要发布诊断。

但是这个程序根据以下C规则具有未定义的行为:

从:

(C99,6.9.1p7)“如果声明符包含参数types列表,则列表还指定所有参数的types;这样的声明符也可用作函数原型,以便稍后调用同一个翻译单元中的相同函数。如果声明者包括一个标识符列表,142)参数的types应在下面的声明列表中声明。

foo函数不提供原型。

从:

(C99,6.5.2.2p6)“如果表示被调用函数的expression式的types不包含原型[…]如果参数个数不等于参数个数,则行为是不确定的。

foo(str)函数调用是未定义的行为。

C没有要求实现为调用未定义的行为的程序发布诊断,但是你的程序仍然是一个错误的程序。

C99标准(6.7.5.3)和C11标准(6.7.6.3)都规定:

标识符列表仅声明该函数的参数的标识符。 函数声明器中的空列表是该函数定义的一部分,指定该函数没有参数。 函数声明器中的空列表不是该函数定义的一部分,它指定不提供有关参数数量或types的信息。

由于foo的声明是定义的一部分,因此声明指定foo需要0个参数,所以调用foo(str)至less在道德上是错误的。 但是如下所述,C语言中存在不同程度的“错误”,编译器在处理某些“错误”时可能会有所不同。

举一个稍微简单的例子,考虑下面的程序:

 int f() { return 9; } int main() { return f(1); } 

如果我用Clang编译上面的代码:

 tmp$ cc tmp3.c tmp3.c:4:13: warning: too many arguments in call to 'f' return f(1); ~ ^ 1 warning generated. 

如果我使用gcc 4.8进行编译,即使使用-Wall,也不会收到任何错误或警告。 以前的答案build议使用-Wstrict-prototypes,它正确地报告f的定义不是原型forms,但这实际上不是重点。 C标准允许以非原型forms的函数定义,如上面的标准,并且标准清楚地表明该定义指定该函数接受0个参数。

现在有一个约束 (C11 Sec。6.5.2.2):

如果表示被调用函数的expression式有一个包含原型的types,则参数个数应与参数个数一致。

但是,这种约束不适用于这种情况,因为函数的types不包含原型。 但是这里是语义部分的后续语句(不是“约束”),它适用于:

如果表示被调用的函数的expression式的types不包含原型…如果参数的数量不等于参数的数量,则行为是未定义的。

因此,函数调用确实会导致未定义的行为(即程序不是“严格符合”)。 但是,该标准只要求一个实现在违反实际约束时报告诊断消息,并且在这种情况下不存在违反约束的情况。 因此,gcc不需要报告错误或警告,以成为“符合实施”。

所以我认为这个问题的答案,为什么gcc允许它呢,是gcc不需要报告任何东西,因为这不是违反约束。 此外,海湾合作委员会并没有声称报告每一种不确定的行为,即使用“墙”或“W”。 这是未定义的行为,这意味着实现可以select如何处理它,而gcc已经select了编译它而没有警告(显然它只是忽略了参数)。