admin管理员组

文章数量:1533101

2024年3月20日发(作者:)

从C/C++源程序到可执行文件要经历两个阶段:(1)编译器将源文件编译成汇编代码,

然后由汇编器(assembler)翻译成机器代码(再加上其它相关信息)后输出到一个个目标文

件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;(2)链接器(linker)

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

C语言提供了一种将多个目标文件打包成一个文件的机制,这就是静态链接库(static

library)。开发者在链接时只需指定程序库的文件名,链接器就会自动到程序库中寻找那些

应用程序确实用到的目标模块,并把(且只把)它们从库中拷贝出来参与构建可执行文件。

链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在

此期间它要维护若干个集合:(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里的目标模块就被简单地丢弃,链接器继续处理下一输入文

件。

(3): 如果处理过程中往D加入一个已存在的符号,或者当扫描完所有输入文件时U非

空,链接器报错并停止动作。否则,它把E中的所有目标文件合并在一起生成可执行文件。

VC带的编译器名字叫,它有这么几个与系统库有关的选项: /ML、/MLd、/MT、

/MTd、/MD、/MDd。这些选项告诉编译器应用程序想使用什么版本的C标准程序库。

/ML(缺省选项)对应单线程静态版的标准程序库();/MT对应多线程静态版标准库

(),此时编译器会自动定义_MT宏;/MD对应多线程DLL版(导入库,

DLL是),编译器自动定义_MT和_DLL两个宏。后面加d的选项都会让编译器

自动多定义一个_DEBUG宏,表示要使用对应标准库的调试版,因此/MLd对应调试版单

线程静态标准库(),/MTd对应调试版多线程静态标准库(),/MDd对

应调试版多线程DLL标准库(导入库,DLL是)。在cl编译出的目

标文件中会有一个专门的区域存放一些指导链接器如何工作的信息,其中有一种就叫缺省

库(default library),这些信息指定了一个或多个库文件名,告诉链接器在扫描的时候也

把它们加入到输入文件列表中(当然顺序位于在命令行中被指定的输入文件之后)。说到这

里,我们先来做个小实验。写个顶顶简单的程序,然后保存为main.c :

/* main.c */

int main() { return 0; }

用下面这个命令编译main.c(什么?你从不用命令行来编译程序?这个......) :

cl /c main.c

/c是告诉cl只编译源文件,不用链接。因为/ML是缺省选项,所以上述命令也相当于:

本文标签: 文件目标链接符号定义