C++从代码到可执行程序
预编译
主要处理以“#”开头的预编译指令
- 删除所有的#define,展开所有的宏定义
- 处理所有的条件预编译指令,如#if、#endif、#ifdef、#elif、#else
- 处理#include预编译指令,将文件内容替换到它的位置,递归进行,文件中包含其他文件
- 删除所有的注释
- 保留所有的#pragma编译指令,编译器需要用到它们。如pragma once是为了防止有文件被重复引用
- 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,和编译时产生编译错误或警告时能够显示行号
编译
把预编译生成的.i或者.ii文件,进行一系列词法分析、语法分析、语义分析、优化后,生成相应的汇编代码文件
- 词法分析:利用类似有限状态机的算法,将源代码程序输入到扫描机中,将其中的字符序列分割成一系列的记号
- 语法分析:语法分析器对由扫描器产生的记号,进行语法分析,产生语法树。由语法分析器输出的语法树是一种以表达式为节点的树
- 语义分析:语法分析器只是完成了对表达式语法层面的分析,语义分析器则对表达式是否有意义进行判断,其分析的语义是静态语义(在编译期能分析),相对应的动态语义是在运行期间才能确定
- 优化:源代码级别的一个优化过程
- 目标代码生成:由代码生成器将中间代码转换成目标机器代码(汇编语言)
- 目标代码优化:对目标机器代码进行优化,寻找合适的寻址方式、使用位移来代替乘法运算、删除多余的指令
汇编
将汇编代码转换成机器可以执行的指令(机器码文件),汇编器的汇编过程相对于编译器来说更简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令对照表翻译过来,经过汇编之后,产生目标文件(与可执行文件几乎一样)
链接
将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序
链接分为静态链接和动态链接:
静态库可能是源代码,动态库可能本身就是一个可执行文件
Makefile编写
对于大的工程通常涉及很多头文件和源文件,编译起来很麻烦,Makefile正是为了自动化编译产生的,Makefile像是编译说明书,指示编译的步骤和条件,之后被make命令解释。
A:B
(tab)<command>
其中A是语句最后生成的文件,B是生成A所依赖的文件,比如生成test.o依赖于test.h,则写成test.o:test.c test.h。接下来一行的开头必须是tab,再往下就是实际命令了,比如gcc -c test.c -o test.o
Makefile的书写非常像shell脚本,可以在文件中定义“变量名 = 变量值”的形式,之后需要使用这个变量时只需要写一个$符号加上变量名即可,当然和shell一样最好用()包裹起语句来
|