众所周知计算机是只能识别二进制指令的,那么我们写得代码计算机究竟是怎么识别的呢?
在ANSI C(标准C)的任何一种实现中,存在两个不同的环境。
第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。
第2种是运行环境,它用于实际执行代码。
翻译环境
- 组成一个程序的每个源文件都会单独通过编译过程分别转换成对应的目标文件。
- 每个目标文件由链接器捆绑在一起,形成一个单一而完整的可执行程序。
- 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中
翻译环境又可以分为:
编译
- 预编译
- 编译
- 汇编
链接
编译
预编译
预编译会生成一个.i为后缀的文件。左图是源程序,右图为经过预编译之后的程序(最上面那几行都是对头文件的引用)。
程序在预编译阶段要做的都是一些文本操作:
- 将代码中的所有注释进行删除;
- 将#define定义的所有的宏和标识符进行替换;
- 对所有头文件进行包含;
- 对所有的预处理指令进行处理。
编译
代码经过预编译阶段的处理后就进入了编译阶段,在这个阶段代码会被翻译成汇编代码放在一个后缀为.s的文件中。
这就是上述代码经过编译阶段后所生成的.s文件中的汇编代码。
在编译阶段程序会进行以下操作:
- 语法分析
- 词法分析
- 语义分析
- 符号汇总
汇编
在汇编阶段代码会被转换成二进制代码。
在 Linux下gcc编译后所产生的目标文件和可执行程序都是以ELF格式进行存储的。而这种格式可以在gcc中用下面的指令所识别(readelf test.o -a):
在汇编阶段程序会进行以下操作:
- 形成符号表;
- 将汇编代码转化成二进制代码。
符号表示什么呢?
在编译阶段有一部是符号汇总下面的代码在经过符号汇总后会生成符号表变成如图所示:
链接
在链接阶段程序会由.o文件转换成可执行程序。
在链接阶段程序会进行以下操作:
- 合并段表;
- 符号表的合成与重定位。
由于每个源文件都会单独生成一个符号表而可执行程序却只有一个所以会进行符号表的合并将名称一样的符号进行合并,并替换无效地址,用上文的程序距离如果impgame.c文件中没有定义ADD函数那么此时程序就会报错。也就是说函数是在链接阶段发现被调用的函数未定义。
运行环境
程序执行的过程:
- 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
- 程序的执行便开始。接着便调用main函数。
- 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
- 终止程序。正常终止main函数;也有可能是意外终止。