预编译阶段:.i
- 删除所有的“#define”,展开所有宏定义并进行文本替换
- 处理所有的条件预编译指令,“#if”,“#ifdef”,“endif” 等,处理“#include”预编译指令,将包含的文件插入到该编译指令的位置
- 删除所有的注释
- 添加行号和文件名标识(以便于编译器产生调试用的符号信息及编译时产生编译错误和警告时显示行号)
- 保留所有的#pragma编译器指令
编译阶段:.s
- 词法分析:识别关键字、标识符、常数、运算符、分隔符等,如:不能以数字作为函数名开头
- 语法分析:根据语言的语法规则将单词符号序列分解成各类语法单位,通过语法分析确定输入串是否构成一个语法正确的程序
- 语义分析:根据代码上下文环境分析程序中各种语法结构的语义信息。如:类型分析和检查错误
- 代码优化:优化的内容如下
常量传播
将编译期间可计算出结果的变量转换成常量,减少了变量的使用。
int main()
{
int nVar = 1;
printf("nVar = %d \n", nVar);
}
变量nVal是一个在编译期间可以计算出结果的变量,借助常量传播代码等价于:
int main()
{
printf("nVar = %d \n", 1);
}
常量折叠
当计算公式中出现多个变量进行计算的情况时,且编译器可以在编译期间计算出结果时,用结果代替所有的常量计算。
int main()
{
int nVar = 1 + 6 - 2 + 1 * 2;
printf("nVar = %d \n", nVar);
}
"1 + 6 - 2 + 1 * 2"的值可以再编译过程中计算出来,所以编译器会将计算的结果代替原表达式
int main()
{
int nVar = 7;
printf("nVar = %d \n", nVar);
}
此时变量nVar是一个在编译期间可计算出结果的变量,在借助“常量传播”等价于
int main()
{
printf("nVar = %d \n", 7);
}
- 汇总符号
- 确定参数类型,确定缺省编译值、可访问权限、参数默认值。
汇编阶段:.o
(可重定位的二进制目标文件)
- 将汇编指令翻译成二进制格式,生成各个section,生成符号表
汇编阶段未处理问题:
- 弱符号
- 符号表,外部符号
- 指令段:虚假的地址和偏移
链接阶段:.exe
- 合并各个section,调整section的起始位移和段大小
- 合并符号表,进行符号解析
- 分配地址和空间
- 符号重定位
运行:
- 建立虚拟地址空间和物理内存的映射(创建映射结构体 PCB)创建页、页表
- 加载指令和数据 (通过load加载器)
- 把程序的入口地址写入下一行指令寄存器