1.1 编译的基本流程

预处理(prepressing) -> 编译(compilation) -> 汇编(assembly) -> 链接(linking)

预处理代码cpp -> 编译代码gcc -> 汇编代码as -> 链接代码ld

一、预处理

程序源码被预处理器cpp处理。 如果是.c文件,则被预处理成.i文件。如果是.cpp文件,则被预处理成.ii文件。

预处理主要实现以下功能:

  1. 展开"#define"宏定义
  2. 处理条件预处理指令,如"#if","#ifdef","#elif","#else","#endif"
  3. 递归处理"#include"指令,讲被包含的文件插入该预处理指令位置
  4. 删除所有注释"/* * /"和"//"
  5. 添加行号和文件名标识,以便编译时编译器产生调试用的行号信息及用于编译时产生的编译错误或警告时能够显示行号
  6. 保留#pragma编译器指令,因为编译器需要使用它们

预处理的命令为:

  • gcc -E hello.c -o hello.i
  • cpp hello.c > hello.i

二、编译

编译是一个复杂的过程,主要有以下阶段:

  1. 词法分析:扫描器(Scanner)利用有限状态机(Finite State Machine)将源代码的字符序列分割成一系列的记号(Token),现有工具为lex
  2. 语法分析:语法分析器(Grammer Parser)利用上下文无关语法(Context-free Grammer)对第一步产生的记号进行语法分析,产生语法树(Syntax Tree)。现有工具为yacc
  3. 语义分析:语义分析器(Semantic Analyzer)检查源代码语义是否合法。如指针乘以浮点数语法合法,语义却不合法。此步骤只能分析静态语义(Static Semantic),与之对于的动态语义(Dynamic Semantic)只有在运行时才能确定是否合法。静态语义分析包括声明、类型的匹配和类型的转换。
  4. 中间语言生成:源代码级优化器(Source Code Optimizer)将整个语法树转换成中间代码(Intermediate Code),然后进行源代码级优化。至此中间代码可以把编译器分为前端和后端,前端和平台无关,后端转换成目标机器代码和平台有关。
  5. 目标代码生成与优化:代码生成器(Code Generator)和目标代码优化器(Target Code Optimizer)把中间代码转换成目标机器代码,再进行优化。

编译的命令为:

  • gcc -S hello.i -o hello.s

对于现代版本的gcc,预处理和编译被合并成一个步骤,使用cc1的程序完成。 对于c++,使用cc1plus来完成。 使用以下命令可看到结果:

  • /usr/lib/gcc/i486-linux-gnu/4.6.1/cc1 hello.c

三、汇编

汇编器把汇编语句转换成机器指令。 这相对比较简单,基本只要逐条翻译就行了。

汇编的命令为:

  • gcc -c hello.s -o hello.o
  • as hello.s -o hello.o 或从.c文件经过前面三个步骤生成 目标文件
  • gcc -c hello.c -o hello.o

四、链接

链接本质上就是修正一些指令对符号地址的引用。链接是日常编译时错误最多的阶段。 链接过程主要包括了:

  1. 地址和空间的分配(Address and Storage Allocation)
  2. 符号决议(Symbol Resolution)(静态链接)或称符号绑定(Symbol Binding)(动态链接)
  3. 重定位(Relocation)

发表评论