【发布时间】:2016-05-09 15:15:28
【问题描述】:
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 标准标识符的链接中的规则。但似乎并非如此。谁能帮我澄清一下?
【问题讨论】:
-
没有得到诊断并不意味着代码是合法的!传统上,编译器一次只查看一个翻译单元(因此在编译时无法检测到不匹配),并且类型信息不会传递到目标文件中,因此链接器将无法检测到不匹配要么。
-
@NateEldredge 尝试一次执行一个文件,而不是在命令行中同时指定两个文件。执行
gcc a.c b.c会一步编译和链接。 C 不进行名称修改,因此函数的名称对于链接器来说看起来完全一样。现在,如果您有三个目标文件都带有myfun怎么办?你会得到一个函数重定义错误。很明显你说的不是真的。 -
虽然
gcc a.c b.c运行编译器和链接器,但它们是两个独立的通道,不共享目标文件以外的信息。链接器能够检测同一符号的多个定义。但在您的示例中,函数myfun仅在mylib.c中定义 一次。文件test.c包含myfun的声明,但没有定义。因此链接器没有错误。 -
这归结为任何在调用函数之前不呈现函数原型的 C 程序都是非常糟糕的编写。同样,任何函数原型和定义不同的 C 程序都写得很糟糕。只是不要编写糟糕的代码,你就不会遇到这样的问题。
-
感谢您的 cmets。实际上,我们正在构建一个根据 C11 标准分析 C 程序的工具。我们在实际的一些程序中发现了这样的问题。我们需要根据C11标准找出处理此类问题的“正确”方式。如果是未定义的行为,那么工具会报错;如果它是依赖于实现的,那么我们可以选择 clang 的方式。