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

Why does mismatched prototype and definition with empty argument list give different results in GCC and Clang?

给定的(简化的)代码片段:

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 编译失败,错误信息如下:

error: number of arguments doesn't match prototype

而 clang 4.0 毫无怨言地翻译了它。

根据 C 标准,哪种实现是正确的?旧式定义 "cancels" 之前的原型是否有效?

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

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

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


引述:

章节 §6.7,P4(约束

All declarations in the same scope that refer to the same object or function shall specify compatible types.

然后,章节 §6.7.6.3,P14,

An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

因此,这构成了约束违规并需要发出诊断。

免责声明:我不是 , but I play one on

如果编译器不发出诊断,则它将不符合要求,如果编译器声称符合要求,则可能被视为错误。

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

An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

因此,foo 的定义没有指定任何参数,而 foo 之前的声明指定了两个参数。

C.2011§6.7.6.3¶15:

For two function types to be compatible, both shall specify compatible return types.146) Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.
146) If both function types are ‘‘old style’’, parameter types are not compared.

因此,foo 的两个声明符不兼容。
该死!来自 :

It's well-established that void foo() does not provide a prototype, even in a definition. There was a DR that answered this explicitly. The type of foo in that definition is void foo(), not void foo(void), and void foo() and void foo(int, int) are compatible types. This answer is incorrect.

上述标准文本中强调的部分是允许参数数量不一致但类型兼容的漏洞。虽然函数定义中指定了一个不带参数的函数,但由于实际上缺少参数类型列表,因此其函数原型中的 foo 类型与函数原型中的 foo 类型实际上不存在不兼容的问题函数定义。

因此,clang 4.0 似乎是正确的,因为没有违反约束。

My original argument becomes invalid, so editing out that part of my original answer.


在评论中,您实际上给出了以下示例:

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) {}

(C11, 6.7p4 Constraints) "All declarations in the same scope that refer to the same object or function shall specify compatible types"

(C11, 6.7.6.3p14) "An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. [...]"

我的意见是违反了 6.7p4 的约束,必须发布诊断。

编辑:

正如 @hvd it is actually not correct. 6.7.6.3p14 does not mean void foo() {} provides a prototype for foo as per DR#317 指出的那样。从这个意义上讲,没有违反 6.7p4 约束,因此 clang 不抱怨是对的。

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

15 For two function types to be compatible, both shall specify compatible return types.146) Moreover, ... If one type has a parameter type list and the other type is specified by a function definition that contains a (possibly empty) identifier list, both shall agree in the number of parameters, and the type of each prototype parameter shall be compatible with the type that results from the application of the default argument promotions to the type of the corresponding identifier. (In the determination of type compatibility and of a composite type, each parameter declared with function or array type is taken as having the adjusted type and each parameter declared with qualified type is taken as having the unqualified version of its declared type.)

和(6.2.7兼容类型和复合类型)

2 All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined

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