为什么gcc允许将参数传递给定义为不带参数的函数?

时间:2012-09-28 15:45:58

标签: c gcc compiler-errors

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

#include <stdio.h>
void foo() {
    printf("Hello\n");
}

int main() {
    const char *str = "bar";
    foo(str);
    return 0;
}

gcc甚至没有发出警告我向foo()传递了太多的参数。这是预期的行为吗?

4 个答案:

答案 0 :(得分:83)

在C中,使用空参数列表声明的函数在被调用时接受任意数量的参数,这些参数受常规算术促销的约束。调用者有责任确保提供的参数适合函数的定义。

要声明一个零参数的函数,您需要编写void foo(void);

这是出于历史原因;最初,C函数没有原型,因为C是从无类型语言B演变而来的。添加原型时,原始无类型声明留在语言中以便向后兼容。

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

  

如果在未指定参数类型的情况下声明或定义函数,则发出警告。 (如果前面有一个指定参数类型的声明,则允许使用旧式函数定义而不发出警告。)

答案 1 :(得分:53)

由于遗留原因,为参数列表声明一个带()的函数实质上意味着“在调用函数时找出参数”。要指定函数没有参数,请使用(void)

编辑:我觉得我在这个问题上因为年老而声名狼借。只是让孩子们知道以前编程的是什么,这里是my first program。 (不是C;它告诉你我们之前必须使用的东西。)

答案 2 :(得分:10)

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

foo(str);

在C中,此代码不违反约束(如果它以void foo(void) {/*...*/}的原型形式定义),并且由于没有约束违规,编译器不需要发出诊断。

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

自:

  

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

foo函数不提供原型。

自:

  

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

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

C不强制实现为调用未定义行为的程序发出诊断,但程序仍然是错误的程序。

答案 3 :(得分:6)

C99标准(6.7.5.3)和C11标准(6.7.6.3)均声明:

  

标识符列表仅声明参数的标识符     功能。函数声明符中的空列表     该函数的定义指定该函数没有     参数。函数声明符中的空列表不是其中的一部分     该函数的定义指定没有关于的信息     提供参数的数量或类型。

由于foo的声明是定义的一部分,因此声明 指定foo接受0个参数,因此调用foo(str)为 最不道德的错误。但如下所述,有不同之处 度数&#34;错误&#34;在C中,编译器的处理方式可能不同 某些&#34;错误&#34;。

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

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,我也不会收到任何错误或警告。之前的回答建议使用-Wstrict-prototypes,它正确地报告f的定义不是原型形式,但这不是重点。 C标准允许以非原型形式的函数定义,例如上面的标准,并且标准清楚地声明该定义指定该函数采用0参数。

现在有约束(C11 Sec.5.5.2.2):

  

如果表示被调用函数的表达式具有包含原型的类型,则参数数量应与参数数量一致。

但是,此约束不适用于此情况,因为函数的类型不包含原型。但是这里是语义部分中的后续语句(不是&#34;约束&#34;),它确实适用:

  

如果表示被调用函数的表达式具有不包含原型的类型     ...如果参数数量不等于参数数量,则行为未定义。

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

所以我认为问题的答案,为什么gcc允许它?是gcc不需要报告任何内容,因为这不是违反约束。此外,即使使用-Wall或-Wpedantic,gcc也不会声称报告各种未定义的行为。它是未定义的行为,这意味着实现可以选择如何处理它,并且gcc选择在没有警告的情况下编译它(显然它只是忽略了参数)。