编译和链接都发生了什么?
1 前言
一般我们在写代码的时候,写完直接点击编译运行,但是却并未思考,编译这个过程到底发生了什么;实际上,当前的很多IDE(如Visual Stidio)都将这个过程忽略了;也就是从源文件(.cpp)到可执行文件(.exe)中间的过程我们并未过多了解;
.cpp文件到可执行文件.exe中间的过程如下图所示(预编译–>编译–>汇编–>链接)4个步骤
2 预编译(Propressing)
预编译主要就是处理宏定义,将其展开等工作
- 从.cpp文件变成.i文件
- 主要是处理源代码文件中的预编译指令,例如#xxxx
1) 预编译指令的展开
2) 添加行号和文件名等调试信息
3) 删除注释
4) 处理编译器指令如#pragma - 预编译后的文件不包含任何宏定义
3 编译(Compliation)
编译主要负责识别源代码,并将其转换成优化后的汇编代码
- 从.i文件变成.s文件(汇编代码文件)
- 主要工作是对.i文件进行一系列的词法分析等,目的是让编译器知道这写的代码是啥
1) 词法分析: 主要是将源代码指令识别分解成一个个符号、关键字、数字等
2) 语法分析: 主要是分析表达式是否合理,例如不允许指针和指针相乘运算等
3) 语义分析: 在编译阶段主要是静态语义;工作是声明和类型的匹配、类型的转换,经过语义分析之后,所有的表达式都被标注了相对应的类型
4) 中间语言生成: 将上述的源代码转换成适合用于转换为汇编代码的中间代码;我的理解是将这些代码变得更加简洁(这其中也必然存在拆分动作)
5) 目标代码生成和优化: a) 所谓的目标代码,其实是优化后的汇编代码; b) 将中间代码转化成汇编代码; c) 再将汇编代码进行优化; d) 最后成为.s文件
4 汇编(Assembly)
汇编是一个理解起来比较简单的过程:对应不同的机器的指令集,将汇编代码一条一条翻译为机器指令
- 将.s文件变成.o文件(也即目标文件)
- 汇编器将上一步骤优化后的汇编代码一条一条的转化为机器指令
- 机器指令(形如:010101111000110101…)是唯一可以被计算机读懂的指令
5 链接(Linking)
链接主要负责将目标文件(.o文件)和库文件整合起来,就像拼图一样
- 将.o文件和运行时库(Runtime Library)中的目标文件组装成可执行的.exe文件
- 这个过程就像是拼图一样,将相互需要的文件进行组装(比如a.o文件中调用了b.o文件中的函数等)
备注:什么是运行时库(Runtime Library)?
- 库其实是支持程序运行的基本函数的集合
- 是一组目标文件的包,就是一些常用的代码编译成目标文件后打包存放就是所谓的库