对于平常的程序开发而言,我们很少会关注到程序中的编译与链接过程,因为一般通常的开发环境都为流行的集成开发环境,而集成开发环境一般都会讲编译与链接的过程一步完成,通常将这种编译与链接合并在一起的过程叫为构建。在这样的开发过程中,我们往往会被这些复杂的集成工具所提供的强大功能所迷惑,很多系统软件的运行机制与机理会被隐藏,因此程序的很多莫名的错误会让我们无所适从,面对程序运行时种种的性能问题瓶颈会让我们无所适从。如果我们能够深入了解软件运行背后的机制与机理,有些问题解决起来将会游刃有余。
本篇将主要讲解被集成开发环境所隐藏的编译与链接过程,如文章中有错误请谅解,本文借鉴与《程序员的自我修养》中的第二章——编译和链接。希望与我一样的编程中的新手,大家在练习编程技巧是可以同时注重编程背后的机理。
四个阶段
在我们运行一段代码时,无论代码的复杂程度都将会进行两个步骤,分别为编译和链接,其中的编译过程又将分为三个阶段,预处理阶段、编译阶段和汇编阶段。下面将分别介绍各个过程。
预处理阶段
该阶段预编译器cpp会将c程序源代码文件以其相关的头文件预编译成一个.i文件。c++程序预编译后的文件扩展名为.ii。
预处理阶段主要处理那些源代码文件中的以”#”开始的预编译指令,例如”#include”、”#define”等,主要处理规则如下:
* 展开所有由”#define”定义的宏与标识符,并将”#define”删除
* 处理所有的条件预编译指令,例如”#if”、”#ifdef”、”#elif”、”#else”、”#endif”
* 处理”#include”预处理指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的也就是说被包含的文件还可能包含其他文件
* 删除所有的注释”//”、”/* */”
* 保留所有的的”#pragma”编译器指令,因为编译器要使用它们
编译阶段
编译阶段会将预处理完的文件进行一系列的词法分析、语法分析、语义分析以及生成相应的汇编代码,给过程会生成一个.s文件。编译阶段往往是整个构建的核心部分,也是最复杂的部分之一。
汇编阶段
汇编阶段就是把编译所生成的汇编代码翻译成机器可识别的机器指令,每一个汇编代码都会对应一条机器指令。所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了。经过汇编阶段会生成目标文件,目标文件中所存放的也就是与源程序等效的目标的机器语言代码。目标文件有段组成,一般一个目标文件中至少有两个段:
代码段:该段中所包含的主要是程序的指令。该段中一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中用到的各种全局变量或静态变量的数据。一般数据段都是可读,可写,可执行的。
链接阶段
由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。链接阶段的主要过程就是将有关的目标文件彼此相连接,也就是将一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够将操作系统装入执行的统一整体。