c++编译链接过程

大家都知道,从 C/C++ 源程序到可执行文件要经历两个阶段 :

(1) 编译器将源文件编译成汇编代码,然后由汇编器 (assembler) 翻译成机器指令 ( 再加上其它相关信息 ) 后输出到一个个目标文件 (object file, VC 的编译器编译出的目标文件默认的后缀名是 .obj) 中;

(2) 链接器 (linker) 将一个个的目标文件 ( 或许还会有若干程序库 ) 链接在一起生成一个完整的可执行文件。

编译器编译源文件时会把源文件的全局符号 (global symbol) 分成强 (strong) 和弱 (weak) 两类传给汇编器,而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢?

extern int errorno;

int buf[2] = {1,2};

int *p;

int main()
{
return 0;
}

其中 main 、 buf 是强符号, p 是弱符号,而 errorno 则非强非弱,因为它只是个外部变量的使用声明。

有了强弱符号的概念,我们就可以看看链接器是如何处理与选择被多次定义过的全局符号 :

规则 1: 不允许强符号被多次定义 ( 即不同的目标文件中不能有同名的强符号 ) ;

规则 2: 如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;

规则 3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;

多个目标文件不能重复定义同名的函数初始化了的全局变量,否则必然导致 LNK2005 和 LNK1169 两种链接错误。

C 语言提供了一种将多个目标文件打包成一个文件的机制,这就是静态程序库 (static library) 。开发者在链接时只需指定程序库的文件名(如:XXX.lib),链接器就会自动到程序库中寻找那些应用程序确实用到的目标模块,并把 ( 且只把 ) 它们从库中拷贝出来参与构建可执行文件。几乎所有的 C/C++ 开发系统都会把标准函数打包成标准库提供给开发者使用.

符号解析 (symbol resolution) 阶段,
链接器按照所有目标文件(xxx.obj)库文件(xxx.lib)出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合 :

(1) 集合 E
是将被合并到一起组成可执行文件的所有目标文件集合;

(2) 集合 U
是未解析符号 (unresolved symbols ,比如已经被引用但是还未被定义的符号 ) 的集合;

(3) 集合 D
是所有之前已被加入到 E 中已经定义的符号集合。一开始, E 、 U 、 D 都是空的。
【如果处理过程中往 D 加入一个已存在的符号 ,或者当扫描完所有输入文件时 U 非空,链接器报错并停止动作。否则,它把 E 中的所有目标文件合并在一起生成可执行文件。 】

VC 带的编译器名字叫 cl.exe ,它有这么几个与标准程序库有关的选项 : /ML 、 /MLd 、 /MT 、 /MTd 、 /MD 、 /MDd 。这些选项告诉编译器应用程序想使用什么版本的 C 标准程序库。
/ML( 缺省选项 ) 对应单线程静态版的标准程序库 (libc.lib) ; /MT 对应多线程静态版标准库 (libcmt.lib) ,此时编译器会自动定义 _MT 宏;
/MD 对应多线程 DLL 版 ( 导入库 msvcrt.lib , DLL 是 msvcrt.dll) ,编译器自动定义 _MT 和 _DLL 两个宏。
后面加 d 的选项都会让编译器自动多定义一个 _DEBUG 宏,表示要使用对应标准库的调试版,因此
/MLd 对应调试版单线程静态标准库 (libcd.lib) ,
/MTd 对应调试版多线程静态标准库 (libcmtd.lib) ,
/MDd 对应调试版多线程 DLL 标准库 ( 导入库 msvcrtd.lib , DLL 是 msvcrtd.dll) 。

原文地址:https://www.cnblogs.com/fag888/p/5789112.html