为什么不匹配的原型和定义与空参数列表在GCC和Clang中给出不同的结果?

时间:2017-08-29 18:01:01

标签: c language-lawyer c11

给出(简化)代码片段:

void foo(int a, int b); // declaration with prototype

int main(void)
{
    foo(1, 5); // type-checked call (i.e. because of previous prototype)
    return 0;
}

void foo() // old-style definition (with empty argument list)
{

}

和命令行选项(但是,我检查它们并不重要):

-x c -std=c11 -pedantic -Wall

gcc 7.2无法编译它,并显示以下错误消息:

  

错误:参数数量与原型

不匹配

虽然clang 4.0翻译它没有任何抱怨。

根据C标准,哪种实施方法是正确的?旧式定义"取消"是否有效?以前的原型?

4 个答案:

答案 0 :(得分:16)

  

(C11,6.7p4约束)"同一范围内引用同一对象或函数的所有声明都应指定兼容类型"

  

(C11,6.7.6.3p14)"标识符列表仅声明函数参数的标识符。函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。 [...]"

我认为违反了6.7p4的约束,必须发布诊断。

修改

正如@hvd指出的那样,实际上并不正确。 6.7.6.3p14并不意味着void foo() {}根据DR#317foo提供原型。从这个意义上说,6.7p4约束没有被违反,所以clang是不对的抱怨。

答案 1 :(得分:11)

免责声明:我不是,而是在上播放。

如果编译器没有发出诊断信息,那么它将是不符合的,并且如果编译器声称符合要求,则可能被视为错误。

C.2011§6.7.6.3¶14(强调我的):

  

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

因此,foo的定义没有指定参数,而foo的声明早先指定了两个参数。

C.2011§6.7.6.3¶15:

  

要使两种函数类型兼容,两者都应指定兼容的返回类型。 146)   此外,参数类型列表如果两者都存在,则应在数量上达成一致   参数和省略号终止符的使用;相应的参数应具备   兼容类型。
   146)如果两种函数类型都是“旧样式”,则不比较参数类型。

因此,foo的两个声明者不兼容。
党!来自@hvd's comment

  

  已经确定void foo()即使在定义中也不提供原型。 There was a DR that answered this explicitly.该定义中的foo类型为void foo(),而非void foo(void)void foo()void foo(int, int)为兼容类型。这个答案是不正确的。

标准中上面强调的部分是漏洞,它允许参数数量不一致,但兼容类型。虽然函数定义指定了不带参数的函数,但由于实际上缺少参数类型列表,实际上函数原型中foo的类型与函数原型中foo的类型之间不存在不兼容性。功能定义。

因此,clang 4.0似乎没有,因为没有约束违规。

  

我原来的论点变得无效,所以编辑我原来答案的那部分。

在评论中,您实际上提供了以下示例:

void foo () {}

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

并问为什么编译器不会抱怨这种情况。这实际上是here解决的,但简而言之:C.2011仍然接受K& R C函数定义语法。因此,虽然void foo() {}是不带参数的定义,但用于参数验证的原型与void foo();相同,因为空参数列表被解析为K& R.强制进行适当参数检查的现代C语法将改为使用void foo(void) {}

答案 2 :(得分:7)

来自C标准(6.7.6.3函数声明符(包括原型))

  

15要兼容两种功能类型,两者都应指定   兼容的返回类型.146)此外,......如果一个类型有参数   类型列表,另一种类型由函数定义指定   包含(可能是空的)标识符列表,两者都应该同意   参数的数量,以及每个原型参数的类型   应与应用产生的类型兼容   默认参数促销对应的类型   标识符。 (在确定类型兼容性和a   复合类型,用函数或数组类型声明的每个参数是   采取调整后的类型和声明的每个参数   限定类型被视为具有其不合格的版本   声明的类型。)

和(6.2.7兼容型和复合型)

  

2所有涉及同一对象或功能的声明均应   兼容型;否则,行为未定义

因此,问题中显示的程序具有未定义的行为。编译器可以像GCC那样发出诊断消息。

答案 3 :(得分:5)

我没有引用标准编辑:参考C11,章节6.7.6.3/P14 ),但根据我的理解, gcc是正确的,因为你自相矛盾。

承诺在函数定义中,在声明列表中,您将有两个类型int的参数,但它们不存在。在函数定义的情况下,空列表意味着函数不应该参数。因此存在违规约束,gcc投诉是正确的。

clang似乎是一个问题,至少不会产生警告。

行情:

章节§6.7,P4( 约束

  

同一范围内引用同一对象或函数的所有声明都应指定兼容类型。

然后,章节§6.7.6.3,P14,

  

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

因此,这构成违反约束并保证发出诊断。