从预处理到链接

Posted Aline2021-yxz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从预处理到链接相关的知识,希望对你有一定的参考价值。

旨在理解从预处理到链接的过程,不会涉及很底层的东西

预处理

预处理阶段由预编译器参与,主要做了这些事情:

  • 头文件的包含,将被包含文件插入到#include的位置,因为文件中可能还包含其他文件,所以这个过程是递归进行的;
  • 宏定义的替换,将所有使用到宏的地方用宏内容替换,并且删掉宏定义;
  • 条件编译,处理所有条件编译指令,如 #else,#if等等;
  • 注释的删除,删掉所有注释

//预处理阶段做的事情是不归属于编译器的,由独立的预处理器进行

编译

编译是核心,大部分工作都在编译阶段完成,也是本文的重点
编译从整体上看好像就做了一件事:将源代码转换为汇编代码
为什么需要编译?
因为机器只看的懂0和1组成的序列,只有通过这一过程产生汇编代码,再通过汇编就可以转换为机器语言

但其实编译又可以分为如下六个步骤:

  • 扫描
  • 语法分析
  • 语义分析
  • 源代码优化
  • 目标代码生成
  • 目标代码优化

扫描:将源代码输入到一个扫描器中,扫描器会有一个算法,将源代码字符序列转换为各种记号

  • 关键字
  • 标识符,放到符号表里面
  • 特殊符号
  • 字面值(常量)),放到文字表里面
    这里的符号表和文字表都是一种数据结构。

语法分析:每个语言都有自己的语法,相应的编译器在语法分析的时候就是按照语言的语法进行分析,因为上一步扫描生成了很多记号,再这里会通过一种算法使用这些记号构建一颗语法树,如果出现语法错误就会报错,例如:括号不匹配,括号使用错了等等。

语义分析:语法正确不一定有意义,例如指针相乘。语义分析会进行类型检查,比如float给int变量赋值,需要进行隐式类型转换,那么在语义分析这里就会进行,有错误的话会报错,注意:语义分析只会分析在编译期间语义能够确定的表达式。

在介绍下一步骤之前先说一下编译器,编译器通常被分为前端和后端,前端包含源码级优化器,后端包含目标代码生成器目标代码优化器

源代码优化:这个过程会将源代码中一些可以简化的东西进行执行,比如:

int c=2+3;

这个例子在源代码优化部分就会将2+3转换为5,源代码得到优化。
我们上面说了,在语法分析的时候会建立一颗树,那么在树上面修改内容是很不方便的,所以会产生中间代码。

目标代码生成:源代码已经是最简,那么就会生成目标代码,例如汇编代码。

目标代码优化:这里优化的主要是寻址方式,删除多余指令,使用位运算代替乘法等等。

注意:编译期间并不会在栈上为变量分配空间,而是指定一套内存使用方案,等到运行的时候才拿出来使用这个方案,才真正的为变量在栈上分配空间。

汇编

汇编做的事情比较简单,就是将编译产生的汇编代码转换为机器可以看懂的01序列,这一步骤不需要对指令由删除什么的,只需要对照汇编表翻译就行了。

链接

链接也是很重要的一部分,主要是将文件和库链接在一起。
实际工程中,可能需要多个源文件,如果我在一个源文件中使用了另一个源文件的函数,如果没有链接,那我就无法找到函数的起始地址,也就call失败了,那程序就无法运行,所以链接也很重要。

本文大多数观点都来源于《程序员自我修养》一书,如果你想深入了解不妨去看看此书。

以上是关于从预处理到链接的主要内容,如果未能解决你的问题,请参考以下文章

在 Android 中使用片段时处理后按

从其他片段(如导航链接)更改片段

如何使用多个 NavHost 片段创建深层链接

如何从一个片段移动到一个活动

微信小程序代码片段

为啥尽管源代码没有变化,但从一个系统到另一个系统的片段数量却有很大差异?