C11标准谈论标识符的链接,但没有明确讨论链接翻译单元的规则。我的问题是通过使用clang编译两个简单的例子来实现的。
这是我的第一个例子,它有两个相同功能的声明但具有不兼容的类型:
//testall.c
extern char myfun(void*);
int main(){
char a='c';
a=myfun(&a);
}
char myfun(char*c){
return *c;
}
然后我运行命令:$ clang -std = c11 testall.c
而clang报告错误:
testall.c:9:10:错误:'myfun'的冲突类型
char myfun(char * c){
^
testall.c:2:17:注意:先前的声明在这里 extern char myfun(void *);
^
生成1个错误。
我理解这个错误,因为void指针和指向char的指针是不兼容的类型 令我困惑的是,当我将两个声明分成两个不同的翻译单元然后将它们链接成一个时,clang不会报告任何错误:
//test.c
extern char myfun(void*);
int main(){
char a='c';
a=myfun(&a);
}
// mylib.c
char myfun(char*c){
return *c;
}
然后我运行这个命令:$ clang -std = c11 test.c mylib.c。
clang编译并链接两个翻译单元而不报告任何错误或警告。
我认为链接两个翻译单元遵循第6.2.2节C11标准的标识符链接中的规则。但似乎事实并非如此。任何人都可以帮我澄清一下吗?
答案 0 :(得分:3)
这只是未定义的行为。 C11没有说明链接器或它们如何组合多个翻译单元。实际上,这不是问题,因为会有一个包含myfun()
函数声明的头文件,其中包含这两个C文件。
答案 1 :(得分:2)
在包含两个单独文件的示例中,行为未定义。这是我的“案例”,基于C11标准:
C11 6.2.2(4):
对于使用存储类说明符extern声明的标识符... 如果没有先前的声明可见,或者如果先前声明 声明指定没有链接,那么标识符有外部 键。
在test.c
中,myfun
具有外部链接,因为它是使用extern
声明的,并且没有先前的声明可见。
C11 6.2.2(5):
如果函数的标识符声明没有存储类 说明符,它的链接确定就像声明它一样 存储类说明符extern。
在mylib.c
中,声明myfun
没有存储类说明符,因此它就好像是用extern
声明的,因此它在此转换单元中也有外部链接
C11 6.9.1。(7)[功能定义]:
如果声明符包含参数类型列表,则列表也是如此 指定所有参数的类型;这样的宣言员也 作为函数原型,用于以后调用相同的函数 在同一个翻译单元
因此myfun
中mylib.c
的定义也是myfun
的声明(如果您有任何疑问)。
C11 6.2.2(2):
在构成一个翻译单元和库的集合中 整个程序,每个特定标识符的声明 外部链接表示相同的对象或功能。
因此,两个myfun
表示相同的功能。
C11 6.2.7(2):
所有引用同一对象或函数的声明都应该 兼容型;否则,行为未定义。
myfun
的两个声明有不兼容的类型(我可以证明
如果你想,但这就是为什么Clang在单个文件中抱怨的原因
case。)因此整个程序的行为是不确定的。
请注意,6.2.7(2)不是约束,因此Clang不是 违规时需要发出诊断信息。然而, 在单个文件的情况下,存在实际的约束违规, 如下所示,在“约束”标题下:
C11 6.7(4):
引用同一对象的同一范围内的所有声明 或函数应指定兼容类型。
因此Clang必须在这种情况下发布诊断。