考虑以下C程序:
int f() { return 9; }
int main() {
int (*h1)(int);
h1 = f; // why is this allowed?
return h1(7);
}
Run Code Online (Sandbox Code Playgroud)
根据C11标准,Sec.6.5.16.1,在简单的指配中,"下列之一应持有",列表中唯一相关的一项如下:
左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,左侧指向的类型具有全部右边指出的那种限定词;
此外,这是一个"约束",这意味着,一致的实现必须报告诊断消息(如果它被违反).
在我看来,在上述程序的赋值中违反了这种约束.赋值的两端都是函数指针.所以问题是,两种功能类型是否兼容?第二节回答了这个问题.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$
Run Code Online (Sandbox Code Playgroud)
顺便说一句,如果f被声明为"int f(void)...",两个编译器都会发出警告,但根据我对上述标准的阅读,这不应该是必要的.
问题:
Q1:赋值"h1 = f;" 在上面的程序中违反了约束"两个操作数都指向兼容类型的限定或不合格版本"?特别:
Q2:表达式"h1 = f"中的h1类型是某些函数类型T1的指向T1的指针.究竟什么是T1?
Q3:表达式"h1 = f"中的f类型是某些函数类型T2的指向T2的指针.究竟什么是T2?
问题4:T1和T2兼容吗?(请引用标准或其他文件的相应部分以支持答案.)
Q1',Q2',Q3',Q4':现在假设f的声明改为"int f(void){return 9;}".再次回答问题1-4这个程序.
根据C11标准(7.27.2.5),有一个timespec_get指定的功能time.h.我已经尝试了几个编译器,包括clang和几个版本的gcc,它们应该支持C11,但是这个函数总是缺失.宏TIME_UTC也缺失了.
这是一个测试文件mytime.c:
#include <time.h>
#include <stdio.h>
int main() {
printf("C version: %ld\n", __STDC_VERSION__);
fflush(stdout);
struct timespec ts;
timespec_get(&ts, TIME_UTC);
}
Run Code Online (Sandbox Code Playgroud)
和使用Clang的输出:
$ cc --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
$ cc -std=c11 mytime.c
mytime.c:9:3: warning: implicit declaration of function 'timespec_get' is invalid in C99
[-Wimplicit-function-declaration]
timespec_get(&ts, TIME_UTC);
^
mytime.c:9:21: error: use of undeclared identifier 'TIME_UTC'
timespec_get(&ts, TIME_UTC);
^
1 warning and 1 error generated.
Run Code Online (Sandbox Code Playgroud)
timespec_get …