第2章 编译和链接

通常的应用程序,程序员很少会去关注编译和链接的过程,应为IDE一般都为我们做了这些工作,只需要一键或者一条指令就可以完成编译和链接的工作。通常把这种将编译和链接合并到一起的工作叫做构建
下面我们按书中章节总结一下编译和链接

2.1 被隐藏的过程

一个简单的c语言程序helloWorld.c如下:
第2章 编译和链接
在Linux中使用gcc编译上述源程序并运行:
第2章 编译和链接

事实上上述过程可以分解为四个步骤,分别是预处理(Prepressing)编译(Compolation)汇编(Assembly)链接(Linking),如下图所示:
第2章 编译和链接


2.2.1 预编译

预编译的一些主要工作如下:
1. 将所有的“#define”删除,并且展开所有的宏定义。
2. 处理所有的条件编译指令,比如“#if”、“#ifdef”、“#elif”、“#else”、“#endif”。
3. 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置,这个过程是递归进行的,也就是说被包含的文件可能还包含其它文件。
4. 删除所有的注释“//”、“/**/”。
5. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时能够显示行号。
6. 保留所有的#pragma编译器指令,因为编译器须要使用他们。
保留经过预编译后的.i文件不包含任何宏定义,因为所有的宏已经被展开,并且包含的文件也已经被插入到.i文件中。所以当我们无法判断宏定义是否正确或头文件包含是否正确时,可以查看预编译后的文件来确定问题。

2.1.2 编译

书中对编译下的定义是这样的:把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生产相应的汇编代码文件。
在Linux里上述的编译过程就相当于下面的命令:
第2章 编译和链接
不同的语言可能对应不同的编译、汇编、链接命令。但是它们只是预编译程序、汇编器、链接器的包装而已
## 2.1.3汇编 ##
汇编是将汇编代码转换成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。
Linux里上面的汇编过程可以调用汇编器as来完成,本人测得书中所说下面两条命令都会报错,可能与我安装的Linux或者gcc版本有关
第2章 编译和链接
但是下面这条是能够正确执行的,正确输出了目标文件:
第2章 编译和链接
## 2.1.4链接 ##
链接的命令比较复杂,这里直接贴出书中给的命令:
第2章 编译和链接


2.2编译

直观来看,编译就是使用编译器将高级语言翻译成机器语言的一个过程。由于本书的重点并不是将编译,所以在此仅将书中的一个描述编译过程的图列出来:
第2章 编译和链接


2.3链接器年龄比编译器长

这个标题其实不难理解,试想一下,最早出现的是机器语言010101,然后有了汇编,这时候其实是不需要编译的,因为编译是高级语言向汇编转化,最后生成机器语言的过程,当然最初的时候不需要编译器,但是把各个程序模块“组合”成一个可执行的程序,链接是必不可少的,所以如标题所说“链接器年龄比编译器长”。
链接做了什么呢?我总结了一下书中所讲述的内容,链接的工作就是确保“组合”成一个可执行程序的各个程序模块中的变量符号和函数符号在可执行程序中都有唯一的地址。而各个模块的程序并不是一成不变的,比如某几行C++代码的中间又加入了几条语句,那么这时候“组合”到可执行程序中时上下文中的符号地址就可能发生变化,这个过程就叫“重定位”,事实上链接要解决的就是程序地址重定位的问题