编译过程 - ShenYj/ShenYj.github.io GitHub Wiki

编译过程

从原始的代码到最终的可执行文件,经历了四个过程

预处理

Prepressing

预编译过程主要处理那些源代码文件中以#开始的预编译指令。比如 #inclue#define

预处理的主要处理规则

  1. 将所有的#inclue 删除,并展开所有的宏定义
  2. 处理所有条件与编译指令,比如#if#ifdef#elif#else#endif
  3. 处理#inclue 预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
  4. 删除所有的注释///**/
  5. 添加行号和文件名标识,比如#2 ”hello.c“ 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编辑错误或警告时能够显示行号。
  6. 保留所有的 #pragma编译器指令,因为编译器须要使用它们。

预处理示例

  • 使用 gcc 编译器

    gcc -E hello.c -o hello.i

    -E 表示进行预编译

    cpp hello.c > hello.i

编译

Compilation

编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析以及优化后生产相应的汇编代码文件,这个过程往往使我们所说的整个程序构建的核心部分

编译过程

  • 扫描
  • 语法分析

    实现词法分析的程序如:lex
    实现语法分析的程序如:yacc(Yet Another Compiler Compiler)

  • 语义分析

    由语义分析器(Semantic Analyzer)完成,编译器所能分析的语义是静态语义(Static Semantic),与之对应的动态语义(Dynamic Semantic)只有在运行期才能确定

  • 源代码优化
  • 代码生成
  • 目标代码优化

编译示例

  • 使用 gcc 编译器, 将预处理生成的 ir 文件编译为汇编文件

    gcc -S hello.i -o hello.s

    gcc -S hello.c -o hello.s

    都可以得到汇编文件

    这里 gcc 编译器调用的是 cc1

    • 对于 C++来说,对应的程序是 cc1plus
    • Object-C 对应的程序是 cc1obj
    • fortran 是 f771
    • java 是 jc1

    所以实际上 gcc 这个命令指示这些后台程序的包装,它会根据不同的参数要求去调用预编译编译程序 cc1、汇编器 as、链接器ld

汇编

Assembly

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令,所以汇编器的汇编过程相对于编译器来讲比较简单,它没有复杂的语法,也没有语义,也不需要做指令优化,只是根据汇编指令和机器指令的对照表一一翻译就可以了

汇编示例

  • 使用 as

    as hello.s -o hello.o

    或使用 gcc

    gcc -c hello.s -o hello.o

    直接从源码预编译、编译和汇编直接输出目标文件

    gcc -c hello.c -o hello.o

链接

Linking

链接示例

  • e.g.

    ld -static crt1.o crti.o ... -start-group -lgcc -lgcc_eh -lc-end-group crtend.o crtn.o
⚠️ **GitHub.com Fallback** ⚠️