【发布时间】:2014-09-04 19:17:12
【问题描述】:
考虑以下 C 程序:
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
根据 C11 标准,Sec. 6.5.16.1,在一个简单的赋值中,“以下之一应成立”,列表中唯一相关的如下:
左操作数具有原子、限定或非限定指针类型,并且(考虑到左操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,并且由left 具有 right 指向的类型的所有限定符;
此外,这是一个“约束”,这意味着,如果违反了一致性实现,则必须报告诊断消息。
在我看来,上述程序的赋值违反了这个约束。赋值的两边都是函数指针。那么问题来了,这两种函数类型是否兼容?这在 Sec 中得到了回答。 6.7.6.3:
对于要兼容的两个函数类型,两者都应指定兼容的返回类型。146) 此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面达成一致;相应的参数应具有兼容的类型。如果一种类型具有参数类型列表,而另一种类型由不属于函数定义的函数声明符指定且包含空标识符列表,则参数列表不应有省略号终止符,并且每个参数的类型应与应用默认参数提升所产生的类型兼容。如果一种类型具有参数类型列表,而另一种类型由包含(可能为空)标识符列表的函数定义指定,则两者在参数数量上应一致,并且每个原型参数的类型应与类型兼容这是将默认参数提升应用于相应标识符的类型而产生的结果。
在这种情况下,其中一种类型,即 h1 的类型,具有参数类型列表;另一个,f,没有。因此,上面引用中的最后一句话适用:特别是“两者都应在参数数量上达成一致”。显然 h1 采用一个参数。 f呢?以下几点发生在上述之前:
作为该函数定义一部分的函数声明器中的空列表指定该函数没有参数。
很明显 f 需要 0 个参数。所以两种类型在参数个数上不一致,两种函数类型不兼容,赋值违反约束,应该发出诊断。
但是,gcc 4.8 和 Clang 在编译程序时都不会发出警告:
tmp$ gcc-mp-4.8 -std=c11 -Wall tmp4.c
tmp$ cc -std=c11 -Wall tmp4.c
tmp$
顺便说一句,如果 f 被声明为“int f(void) ...”,两个编译器都会发出警告,但根据我对上述标准的阅读,这不是必需的。
问题:
Q1: 是否赋值“h1=f;”在上面的程序中违反了约束“两个操作数都是指向兼容类型的合格或不合格版本的指针”?具体来说:
Q2:对于某些函数类型 T1,表达式“h1=f”中的 h1 类型是指向 T1 的指针。 T1 到底是什么?
Q3:对于某些函数类型 T2,表达式“h1=f”中的 f 类型是指向 T2 的指针。 T2究竟是什么?
Q4:T1 和 T2 兼容类型吗? (请引用标准或其他文件的适当部分来支持答案。)
Q1', Q2', Q3', Q4':现在假设 f 的声明更改为“int f(void) { return 9; }”。再次回答此计划的问题 1-4。
【问题讨论】:
-
如果我把它放入 clang 我得到:functCheck.cxx:4:6: error: assignmenting to 'int (*)(int)' from in compatible type 'int ()': different number of参数 (1 vs 0) h1 = f; // 为什么允许这样做... ^ ~ 1 个错误生成。
-
@user2950041:这是一个 C 问题。 C 的声明、原型和定义的概念与 C++ 不同。
-
@user2950041:实际上,编译器确实在乎。 Clang 从文件扩展名推断语言。 (例如,您可以将模板放入您的
cxx文件中,并使用cc进行编译;如果您将文件命名为foo.xyz,它将无法编译)。 -
并不是我在上面的引用中找到任何支持这一点的东西,但是在 plain old c 中,一个空参数列表意味着您可以提供任意数量的参数。 IE。无效 f() != 无效 f(无效)。我对 C 的深层地牢不太了解 f() == f(...),但我怀疑它,因为 varargs 至少需要第一个参数来挂钩。
-
@user2950041 我终于认为这是一个错误,在 gcc Bugzilla 中提交一个错误怎么样?
标签: c function pointers language-lawyer c11