c-C11中链接翻译单元的规则是什么



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编译并链接两个翻译单元,而不报告任何错误或警告。

我认为链接两个翻译单元遵循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.c0中对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必须在这种情况下发布诊断。

最新更新