给出(简化)代码片段:
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标准,哪种实施方法是正确的?旧式定义"取消"是否有效?以前的原型?
答案 0 :(得分:16)
(C11,6.7p4约束)"同一范围内引用同一对象或函数的所有声明都应指定兼容类型"
和
(C11,6.7.6.3p14)"标识符列表仅声明函数参数的标识符。函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。 [...]"
我认为违反了6.7p4的约束,必须发布诊断。
修改强>
正如@hvd指出的那样,实际上并不正确。 6.7.6.3p14并不意味着void foo() {}
根据DR#317为foo
提供原型。从这个意义上说,6.7p4约束没有被违反,所以clang是不对的抱怨。
答案 1 :(得分:11)
免责声明:我不是language-lawyer,而是在stackoverflow上播放。
如果编译器没有发出诊断信息,那么它将是不符合的,并且如果编译器声称符合要求,则可能被视为错误。
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的空列表 该函数的定义指定没有关于数量或类型的信息 提供参数。
因此,这构成违反约束并保证发出诊断。