编译和链接
翻译环境和运行环境:
在任意 ANSI C 的任意实现中,存在两个环境: 翻译环境和执行环境
- 翻译环境:在这个环境下源代码被转化成可执行程序
翻译环境包括编译和链接
注意:在一个项目中可能出现多个.c文件。 编辑规则:这些.c文件都单独经过编辑器,形成对应的目标文件。 多个目标文件和链接库一起通过链接器形成可执行程序。 链接库是运行时库或者第三方库。 在windows系统的目标文件的后缀为.obj 在linux系统下的目标文件的后缀为.o
- 编译:包括 预处理 , 编译 , 汇编
Test.c ——>预处理——>test.i ——> 翻译 ——> test.s ——> 汇编 ——> test.o(即目标文件,该处为linux系统,如果是Windows系统的话,test.o应改为test.obj)
预处理: 预处理主要处理那些以#开头的预处理指令。(以#开头的都是预处理指令,都是在预处理时进行删除的,但也有个例,即#pragma)
处理规则如下:
I. 将所有的#define删除,并展开宏定义
II.处理所有的条件编辑指令。(例如#if…#endif 等等)
III.处理#include 的预处理指令。将包含的头文件的内容插入到预处理指令的位置。这个过程是递归进行的,也就意味着,头文件中可能包括着其他头文件(例如在自定义的”test.h”的头文件中可能也包括着<stdlio.h>的头文件)
IV.删除所有注释。(实质上是用一个空格,代替这段注释)
V.添加行号和文件标识,以便后续的调试
VI.保留所有的#pragma的预处理指令,以便后续的使用
注意:经过预处理后的.i文件中,不再含有宏定义,因为宏已经展开。且包含的头文件都被插入到.i文件中了。因此我们在想确定宏和头文件是否成功,我们可以去查看.i文件
编译:编译是对预处理后的文件进行一系列的 词法分析 , 语法分析 , 语义分析及优化,从而形成相应的汇编代码文件。
以array[index] = (index+4)*(2+6);举例子
I.词法分析:源代码被输入到扫描器中,扫描器的任务就是进行简单的词法分析,将代码中的字符分割为一系列的标记(关键字,标识符,数字等等)
II.语法分析:将扫描产生的标记进行分析,形成语法树。这些语法树是以表达式为节点的。
III.语义分析
由语义分析器来完成语义分析,即对表达式的语法层面的分析。编辑器能进行的是语义的静态分析。静态语义分析通常包含 声明和类型的匹配 , 类型的转换等。这个阶段会报出语法错误
汇编:汇编器是将汇编代码转变成机械可执行的指令,每个汇编代码几乎都有对应的一条机械指令。就是根据汇编代码和机器指令的对照表一一的进行翻译,不做指令优化。 并形成符号表
- 链接: 链接是把多个文件链接在一起从而达到可执行程序。
链接包括 地址和空间的分配 , 符号决议 和 重定位
链接解决的多文件,多板块之间相互调用的问题
这些头文件都是独立形成对应的目标文件, 如图Add.c中的函数Add有一个地址,test.c中的调用了外部函数Add,但由于这些文件都是独自编译的,因此在test.c中的Add是不知道地址的,因此先搁置它。然后在后续链接器通过符号Add从而在其他模块中进行查找其地址,然后把test.c引用的外部函数Add的地址重新修正,让它的地址为真正的Add函数的地址。 这个地址修正的过程叫做重定位