2010-07-30 129 views
18

当我在一个头文件中声明一个函数,并将该函数的定义放在其他文件中时,编译器/链接器如何找到该定义?它是否系统地搜索其路径中的每个文件,或者是否有更优雅的解决方案?这在过去几天一直困扰我,我一直无法找到解释。C/C++编译器如何找到头文件中原型的定义?

+0

谢谢你提出这个问题。我想知道同样的事情。 – 2014-11-13 15:58:51

+1

发现这真的很有用:http://www.lurklurk.org/linkers/linkers.html – letmaik 2015-02-06 08:45:56

回答

24

编译器不会这样做,链接器会这样做。

虽然编译器一次只能处理一个源文件,但在调用链接器时,会传递编译器生成的所有对象文件的名称以及用户希望链接的任何库。因此,链接器对可能包含定义的文件集有完整的了解,只需查看那些目标文件的符号表即可。除此之外,不需要做任何搜索。

例如,假设您有foo.h和foo.c定义和实现函数foo(),bar.h和bar.c定义并执行bar()。说bar调用foo,以便bar.c包含foo.h.这个汇编有三个步骤:

gcc -c foo.c 
gcc -c bar.c 
gcc foo.o bar.o -o program 

第一行编译foo.c,产生foo.o.第二个编译bar.c,生成bar.o.此时,在目标文件bar.o中,foo是一个外部符号。第三行调用链接器,链接器将foo.o和bar.o链接到一个名为“程序”的可执行文件中。当链接器处理bar.o时,它会看到未解析的外部符号foo,因此它在链接的所有其他目标文件(在本例中仅为foo.o)的符号表中查找并在foo.o中找到foo,并且完成链接。

使用库会有点复杂,它们在命令行上出现的顺序可能取决于链接器,但它的原理通常相同。

+7

好的答案 - 值得补充的是,至少有一个库 - 标准C库 - 隐式*包含在链接行中。因此,在这种情况下,它不仅会出现在'foo.o'中,而且还会出现在未解析符号的标准C库中。 – caf 2010-07-30 01:43:05

+2

*和*,当与g ++连接时,默认情况下也会获得-lstdC++。 – Bklyn 2010-07-30 03:46:15

14

编译.cpp文件时,编译器输出.obj文件中的两个表:它预期定义的符号列表外部的,以及在该特定的符号列表中模块。

链接程序获取编译器输出的所有.obj文件,然后(顾名思义)链接它们都在一起。因此,对于每个模块,它会查看标记为“外部定义”的符号列表,并查看所有其他模块的符号。

所以它永远只能“搜索”,你告诉它在搜索。

如果无法找到任何其他模块的符号的模块,当你拿到“未定义的引用”错误的。

+0

现在我知道在哪种情况下,我收到“未定义参考”错误。我的意思是说编译器(更确切的说是链接器)找不到预期在其他模块中定义的未解析的外部符号。 – 2016-01-08 09:30:32

0

假设你有一个带有#include foo.h的foo.cpp,也许还有其他包含。头部当然可以有自己的#include -s。

预处理器将从foo.cpp开始,解析#includes并读取标题内容。结果将是来自头文件和foo.cpp“扁平化”的文本。 然后编译器将处理该文本。如果一个变量/函数/ etc应该有 被声明在某个头文件中没有找到,编译器会报错。

基本的观点是编译器必须查看所有的声明作为.cpp和头文件的结果。

相关问题