想必每个程序员的初始代码都是从Helloworld程序开始的,但在这个简单程序的背后,我们往往忽视了编译器为我们做的大量工作...
#include <iostream>
using namespace std;
int main(){
cout<<"Hello World."<<endl;
return 0;
}
上述是使用C++编写的一个简单的HelloWorld程序。通常,在Windows的IDE环境下我们往往只需要点击运行即可看到程序的输出结果,即使在Linux的命令行环境下,我们也只需要输入以下命令即可:
gcc hello.cpp -o hello
./hello
然而,在这一些简单操作的背后其实存在大量的工作:预编译、编译、汇编、链接。
1. 预编译
简而言之,预编译主要用于处理源代码中以"#"开始的预编译指令,如:“#include”、“#define”等。经过预编译,C++程序的.cpp文件将生成一个.ii文件
P.s:预编译过程中,处理所需头文件;展开宏定义;删除注释;添加行号、文件名标识等
2. 编译
简而言之,编译过程主要是将预处理完的文件进行一系列词法分析、语法分析、语义分析以及优化后,生成汇编代码文件(.s文件)。
P.s:
- 词法分析:识别记号(Token)——关键字、标识符、字面量和特殊符号;
- 语法分析:生成语法树——确定运算符优先级、确定运算符含义(部分运算符具有多重含义);
- 语义分析:分析静态语言——编译器剑客确定的语义;
- 中间语言生成:优化代码(编译器前端,与机器无关)
- 目标代码生成与优化:
- 代码生成器——将中间代码转换成目标机器代码
- 目标代码优化器——对生成的目标代码进行优化,如:选择合适的寻址方式、删除多余指令等
3. 汇编
在汇编阶段,汇编器将汇编代码转换成机器可以执行的指令。每一条汇编指令几乎都对应一条机器指令,由于该过程没有复杂的语法、语义,也不需要优化,因此只需要汇编器逐句翻译即可。该过程会生成目标文件(Object file, .o文件)
4. 链接
概括而言,编译器可以将一个源代码文件编译成一个未链接的目标文件,链接器负责最终将这些目标文件链接起来形成可执行文件。
众所周知的是,现如今的代码大都是模块化的,以便于代码的阅读、理解、重用等。因此,链接的作用就是将各模块组合成一个单一的程序。所以,组装模块的过程其实就是链接。其主要内容是将各个模块之间相互引用的部分处理好,使得各个模块之间能够正确的衔接。其主要包括:
- 地址和空间分配;
- 符号决议
- 重定位