彻底理解链接器:一,概念

该系列完整的文章目录:

彻底理解链接器:一,概念

在介绍本章的主题之前,我们先来看几个问题:

问题一

写C/C++的同学应该经常遇到这样的一个Error:

undefined reference to ABC
在遇到这样的问题时你知道这背后到底哪里出问题了吗? 你通常都能顺利解决类似问题吗?

问题二

作为世界上最大的同性交友网站GitHub,里面有很多很棒的项目,一般我们或者直接下载其发布版(release version),或者下载源码自己编译,不管是直接下载发布版还是自己编译,最终都会得到一个(或几个)以.so或者.a为结尾的文件(Windows下为DLL文件或者lib文件),这时你知道该怎么把这些.so或者.a文件引入你自己的项目吗?当然如果你去搜索一下也能得到答案,但是你知道这些答案背后的原理吗?

问题三
你的同学、同事在工作学习中可能不时就会提及到静态链接库动态链接库静态链接动态链接,每次听到这些词汇的时候在你脑海里,A)对此有很清晰的认知;B)一头雾水不知道他们在说些什么,你属于A还是B?

如果你还不能很好的解决上面前两个问题且对于问题三属于B,那么接下来你就要好好看这篇文章啦,解决这几个问题的关键就是这篇文章要介绍的链接器(Linker),虽然现代的集成开发环境IDE比如Visual Studio已经对程序员屏蔽了大部分链接器的工作,但理解链接器将极大提高你对工程的驾驭能力,也许你现在还不是很清楚,读完这篇文章你就能明白啦。

什么是链接器(Linker)

让我们引用*中对链接器的定义:

a linker or link editor is a computer utility program that takes one or more object files generated by a compiler and combines them into a single executable file, library file, or another 'object' file.”
如果你看不太懂没有关系,我来翻译一下,链接器是一个将编译器产生的目标文件打包成可执行文件或者库文件或者目标文件的程序。这个翻译比较拗口,不太好理解,这句话的意思具体如下:
首先是链接器的本质,链接器本质上也是一个程序,本质上和我们经常使用的普通程序没什么不同。
其次是链接器的输入,我们经常使用的程序比如播放器,其输入是一个MP4文件,而链接器的输入是编译器编译好的目标文件(object file,如果你不理解什么是目标文件,请参考之前的文章《不简单的hello world之C标准库》)。
最后是链接器的输出,链接器在将目标文件打包处理后,生成或者可执行文件,或者库,或者目标文件。
从这个定义中能够看出,链接器的作用有点类似于我们经常使用的压缩软WinRAR(Linux下是tar),压缩软件将一堆文件打包压缩成一个压缩文件,而链接器和压缩软件的区别在于链接器是将多个目标文件打包成一个文件而不进行压缩。那么链接器到底是如何工作的呢,我们接着往下看。

链接器可操作的元素

链接器可操作的最小元素是一个简单的目标文件,通常我们写的.c源文件编译后就生成了对应的目标文件,我们写的实现文件比如list.c编译后就生成了对应的目标文件list.o(Windows下为list.obj),这个list.o就是链接器可以操作的最小元素。我们见到的所有应用程序,小到自己实现的hello world程序,大到复杂的比如浏览器,网络服务器等,都是链接器将一个个所需要用到的目标文件汇集起来最终形成了非常复杂的应用程序(Windows下是我们常见的EXE文件,Linux下为ELF文件)。

我们可以把最终的应用程序想象成一座房子,构建房子的最基本的原材料就是砖,房子中各个模块像墙面,地面,屋顶等都是由一块块砖构筑成的。而这里的目标文件就好比构建房子时最基本的砖。房子的各个模块就好比我们是用的静态库,动态库。无论多么复杂庞大的应用程序,对于链接器来说最基本的构建材料都是目标文件。链接器可以将目标文件链接器成为各种库以方便使用,然后链接器将目标文件以及程序依赖的各种库再次链接从而形成最终的可执行文件。

我们具体看一下链接器是如何工作的。