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){生成1个错误。
^
testall.c:2:17:注意:前面的声明在这里
extern char myfun(void*)
我理解这个错误,因为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编译并链接两个翻译单元,而不报告任何错误或警告。
我认为链接两个翻译单元遵循C11标准第6.2.2节标识符链接中的规则。但事实似乎并非如此。有人能帮我澄清一下吗?
这只是未定义的行为。C11没有说明链接器或它们如何组合多个翻译单元。在实践中,这不是一个问题,因为会有一个带有myfun()
2函数声明的头文件,其中包括这两个C文件。
在具有两个独立文件的示例中,行为是未定义的。这是我的"案例",基于C11标准:
C11 6.2.2(4):
对于使用存储类说明符extern声明的标识符。。。如果没有可见的先前声明,或者如果声明未指定链接,则标识符具有外部链接。
在CCD_13中,CCD_14具有外部链接,因为它是用CCD_15声明的,并且没有可见的先前声明。
C11 6.2.2(5):
如果函数的标识符声明没有存储类说明符,其链接的确定方式与用存储类说明符extern。
在CCD_16中,CCD_17是在没有存储类说明符的情况下声明的,因此它就像是用CCD_18声明的一样,因此它在这个转换单元中也有外部链接。
C11 6.9.1.(7)【功能定义】:
如果声明符包含参数类型列表,则该列表指定所有参数的类型;这样的声明人用作以后调用同一函数的函数原型在同一翻译单元中
因此,test.c
0中对CCD_19的定义也是对CCD_21的声明(以防您有任何疑问)。
C11 6.2.2(2):
在构成整个程序,每个特定标识符的声明外部链接表示相同的对象或功能。
因此,两个CCD_22s表示相同的函数。
C11 6.2.7(2):
所有涉及同一对象或函数的声明应具有兼容类型;否则,行为是未定义的。
CCD_23的两个声明具有不兼容的类型(我可以显示如果你愿意的话,但这就是Clang在一个文件中抱怨的原因案例。)因此,整个程序的行为是未定义的。
请注意,6.2.7(2)不是约束,因此Clang不是需要在违反时发布诊断。然而在单个文件的情况下存在实际的约束违反,以下内容出现在"限制条件"标题下:
C11 6.7(4):
同一范围内引用同一对象的所有声明或函数应指定兼容类型。
所以Clang必须在这种情况下发布诊断。