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

Grz*_*ski 17 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)
{

}
Run Code Online (Sandbox Code Playgroud)

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

-x c -std=c11 -pedantic -Wall
Run Code Online (Sandbox Code Playgroud)

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

错误:参数数量与原型不匹配

虽然clang 4.0翻译它没有任何投诉.

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

oua*_*uah 15

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

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

我认为违反6.7p4的约束并且必须发布诊断.

编辑:

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


jxh*_*jxh 11

免责声明:我不是,但我在上玩一个.

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

C.2011§6.7.6.314(强调我的):

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

因此,定义中foo没有指定参数,而声明foo前面指定了两个参数.

C.2011§6.7.6.315:

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

因此,两个声明foo者不兼容.
党!来自@ hvd的评论:

它是公认的void foo(),即使在定义中也不提供原型.有一个DR明确地回答了这个问题.该类型的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; }
Run Code Online (Sandbox Code Playgroud)

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


Vla*_*cow 7

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

15要兼容两种函数类型,两者都应指定兼容的返回类型.146)此外,...如果一种类型具有参数类型列表,另一种类型由包含(可能为空)标识符列表的函数定义指定两者都应在参数数量上达成一致,并且每个原型参数的类型应与从默认参数促销应用到相应标识符类型的结果类型兼容.(在确定类型兼容性和复合类型时,使用函数或数组类型声明的每个参数都被视为具有调整类型,并且使用限定类型声明的每个参数都被视为具有其声明类型的非限定版本.)

(6.2.7兼容型和复合型)

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

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


Sou*_*osh 5

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

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

这似乎是clang中的问题,至少不会产生警告.


行情:

章节§6.7,P4(约束)

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

然后,章节§6.7.6.3,P14,

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

因此,这构成了违反约束并且需要发出诊断.