考虑以下C程序:
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
根据C11标准,第6.5.16.1节,在简单赋值中,“以下情况之一应成立”,并且列表中唯一相关的是以下情况:左操作数具有原子、限定或未限定的指针类型,并且(考虑到在lvalue转换后左操作数将具有的类型)两个操作数都是指向兼容类型的限定或未限定版本的指针,并且左边所指的类型具有右边所指类型的所有限定符;此外,这是一个“约束”,这意味着如果违反该约束,则符合要求的实现必须报告诊断消息。看起来,在上述程序的赋值中违反了这个约束。赋值的两边都是函数指针。所以问题是,这两个函数类型是否兼容? 这在6.7.6.3节中得到回答:为了使两个函数类型兼容,两者都必须指定兼容的返回类型。此外,参数类型列表(如果两者都存在)应在参数数量和省略号终止符的使用方面达成一致;相应参数应具有兼容类型。如果其中一个类型具有参数类型列表,而另一个类型由不是函数定义的函数声明符指定,并包含空标识符列表,则参数列表不得具有省略号终止符,并且每个参数的类型都必须与应用默认参数提升后的类型兼容。如果其中一个类型具有参数类型列表,而另一个类型由包含(可能为空的)标识符列表的函数定义指定,则两者应在参数数量上达成一致,并且每个原型参数的类型都必须与相应标识符的类型应用默认参数提升后的类型兼容。 在本例中,其中一个类型(h1的类型)具有参数类型列表;另一个类型(f)没有。因此,上面引用中的最后一句话适用:特别是,“两者的参数数量应一致”。显然,h1需要一个参数。那么f呢?在上述内容之前,发生了以下情况:函数声明符中的空列表是该函数定义的一部分,指定该函数没有参数。因此,很明显f不需要参数。因此,这两种类型的参数数量不一致,两种函数类型不兼容,赋值违反了约束,并且应发出诊断消息。但是,无论是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: 表达式"h1=f"中h1的类型是某个函数类型T1的指针。T1到底是什么?
Q3: 表达式"h1=f"中f的类型是某个函数类型T2的指针。T2到底是什么?
Q4: T1和T2是兼容类型吗?(请引用标准或其他文件中的适当部分以支持答案。)
Q1',Q2',Q3',Q4':现在假设f的声明被改为"int f(void){return 9;}"。再次回答这个程序的问题1-4。
cxx
文件中放置模板,并使用cc
进行编译;但如果你将文件命名为foo.xyz
,则无法编译) - nneonneo