E 将被合并以组成可执行文件的所有目标文件集合
U 当前所有未解析的引用符号的集合
D 当前所有定义符号的集合
/* my.lib */
#include <stdio.h>
void foo()
{
printf("%s","I am from mylib!\n");
}
/* main.obj*/*/
void foo();
int main()
{
foo();
return 0;
}
编译,然后用
link main.obj my.lib
一开始 E 、 U 、 D 都是空集,链接器首先扫描到 main.obj ,把它加入 E 集合,同时把未解析的 foo 加入 U ,把 main 加入 D ,而且因为 main.obj 的默认标准库是 libc.lib ,所以它被加入到当前输入文件列表的末尾。
接着扫描 my.lib ,因为这是个库,所以会拿当前 U 中的所有符号 ( 当然现在就一个 foo) 与 my.lib 中的所有目标模块 ( 当然也只有一个 mylib.obj) 依次匹配,看是否有模块定义了 U 中的符号。
结果 my.lib 确实定义了 foo ,于是它被加入到 E , foo 从 U 转移到 D , my.lib 引用的 printf 加入到 U ,同样地, my.lib 指定的默认标准库是 libcd.lib ,它也被加到当前输入文件列表的末尾 ( 在 libc.lib 的后面 ) 。
不断地在 my.lib 库的各模块上进行迭代以匹配 U 中的符号,直到 U 、 D 都不再变化。很明显,现在就已经到达了这么一个不动点,所以接着扫描下一个输入文件,就是 libc.lib 。
链接器发现 libc.lib 里的 printf.obj 里定义有 printf ,于是 printf 从 U 移到 D ,而 printf.obj 被加入到 E ,它定义的所有符号加入到 D ,它里头的未解析符号加入到 U 。
链接器还会把每个程序都要用到的一些初始化操作所在的目标模块 ( 比如 crt0.obj 等 ) 及它们所引用的模块 ( 比如 malloc.obj 、 free.obj 等 ) 自动加入到 E 中,并更新 U 和 D 以反应这个变化。
事实上,标准库各目标模块里的未解析符号都可以在库内其它模块中找到定义,因此当链接器处理完 libc.lib 时, U 一定是空的。最后处理 libcd.lib ,因为此时 U 已经为空,
所以链接器会抛弃它里面的所有目标模块从而结束扫描,然后合并 E 中的目标模块并输出可执行文件。