静态链接库和动态链接库
显然,实际开发中引入他人编写好的库文件可以省略某些功能的开发环节,提高项目的开发效率。但遗憾的是,类似 myMath.c 这种“开源”的库文件很难找到,多数程序员并不会直接分享源代码,他们更愿意分享库文件的二进制版本——链接库。
所谓链接库,其实就是将开源的库文件(例如上面提到的 myMath.c)进行编译、打包操作后得到的二进制文件。虽然链接库是二进制文件,但无法独立运行,必须等待其它程序调用,才会被载入内存。
一个完整的 C 语言项目可能包含多个 .c 源文件,项目的运行需要经过“编译”和“链接”两个过程:
编译:由编译器逐个对源文件做词法分析、语法分析、语义分析等操作,最终生成多个目标文件。每个目标文件都是二进制文件,但由于它们会相互调用对方的函数或变量,还可能会调用某些链接库文件中的函数或变量,编译器无法跨文件找到它们确切的存储地址,所以这些目标文件无法单独执行。
链接:对于各个目标文件中缺失的函数和变量的存储地址(后续简称“缺失的地址”),由链接器负责修复,并最终将所有的目标文件和链接库组织成一个可执行文件。
注意,一个目标文件中使用的函数或变量,可能定义在其他的目标文件中,也可能定义在某个链接库文件中。链接器完成完成链接工作的方式有两种,分别是:
无论缺失的地址位于其它目标文件还是链接库,链接库都会逐个找到各目标文件中缺失的地址。采用此链接方式生成的可执行文件,可以独立载入内存运行;
链接器先从所有目标文件中找到部分缺失的地址,然后将所有目标文件组织成一个可执行文件。如此生成的可执行文件,仍缺失部分函数和变量的地址,待文件执行时,需连同所有的链接库文件一起载入内存,再由链接器完成剩余的地址修复工作,才能正常执行。
我们通常将第一种链接方式称为静态链接,用到的链接库称为静态链接库;第二种链接方式中,链接所有目标文件的方法仍属静态链接,而载入内存后进行的链接操作称为动态链接,用到的链接库称为动态链接库。
采用静态链接库完成链接操作,存在诸多缺点。首先,可执行文件内部拷贝了所有目标文件和静态链接库的指令和数据,文件本身的体积会很大。当系统中存在多个链接同一个静态库的可执行文件时,每个可执行文件中都存有一份静态库的指令和数据,就会造成内存空间的极大浪费。
和静态链接库相比,动态链接库可以很好地解决空间浪费和更新困难的问题。动态链接库和可执行文件是分别载入内存的,因此动态链接库的体积通常会小一些。当有多个程序使用同一个动态链接库时,所有程序可以共享一份动态链接库的指令和数据,避免了空间的浪费。采用动态链接的方式也可以方便程序的更新和升级,当程序的某个模块更新后,只需要将旧的模块替换掉,程序运行时会自动将所有模板载入内存并动态地链接在一起。