强符号;
规则 3:
如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;
由上可知多个目标文件不能重复定义同名的函数与初始化了的全局变量,否则必然导
致 LNK2005
和 LNK1169 两种链接错误。可是,有的时候我们并没有在自己的程序中发现
这样的重定义现象,却也遇到了此种链接错误,这又是何解?嗯,问题稍微有点儿复杂 ,
容我慢慢道 来。
众所周知, ANSI C/C++ 定义了相当多的标准函数,而它们又分布在许多不同的目标
文件中,如果直接以目标文件的形式提供给程序员使用的话,就需要他们确切地知道哪
个函数存在于哪个 目标文件中,并且在链接时显式地指定目标文件名才能成功地生成可
执行文件,显然这是一个巨大的负担。所以 C 语言提供了一种将多个目标文件打包成一
个文件的机制,这就是静态程序库 (static library) 。开发者在链接时只需指定程序库的文件
名,链接器就会自动到程序库中寻找那些应用程序确实用到的目标模块,并把 (
且只把 )
它们从库中拷贝出来参与构建可执行文件。几乎所有的 C/C++ 开发系统都会把标准函数
打包成标准库提供给开发者使用 (
有不这么做的吗? )
。
程序库为开发者带来了方便,但同时也是某些混乱的根源。我们来看看链接器是如何
解析 (resolve)
对程序库的引用的。
在符号解析 (symbol resolution) 阶段,链接器按照所有目标文件和库文件出现在命令行中
的顺序从左至右依次扫描它们,在此期间它要维护若干个集合 :
(1)
集合 E
是将被合并到一起组成可执行文件的所有目标文件集合;
(2)
集合 U
是未解析符号 (unresolved symbols
,比如已经被引用但是还未被定义的符号 )
的集合;
(3)
集合 D
是所有之前已被加入到 E
的目标文件定义的符号集合。一开始, E
、U
、D 都
是空的。
链接器的工作过程:
(1):
对命令行中的每一个输入文件 f ,链接器确定它是目标文件还是库文件,如果它是目
标文件,就把 f
加入到 E
,并把 f
中未解析的符号和已定义的符号分别加入到 U
、 D 集
合中,然后处理下一个输入文件。
(2):
如果 f
是一个库文件,链接器会尝试把 U
中的所有未解析符号与 f 中各目标模块定
义的符号进行匹配。如果某个目标模块 m
定义了一个 U
中的未解析符号,那么就把 m 加
入到 E
中,并把 m
中未解析的符号和已定义的符号分别加入到 U
、 D 集合中。不断地对
f
中的所有目标模块重复这个过程直至到达一个不动点 (fixed point)
,此时 U
和 D 不再变
化。而那些未加入到 E
中的 f 里的目标模块就被简单地丢弃,链接器继续处理下一输入文
件。