目录
在标准C中的任何⼀种实现中,存在两个不同的环境:
1.翻译环境:该环境源代码被转化为可执行文件
2.运行环境:执行可执行文件生成预期结果
1.翻译环境
分为两个过程:编译和链接
一个或多个源文件依依经过编译器处理后形成对应的目标文件,之后这些目标文件和链接库经过链接器处理后生成可执行文件
编译又可以分为三个阶段:预编译,编译,汇编
1.1编译
预编译:
此阶段主要是由预处理器对预处理指令(#include、#define和#if)进行处理,简单来讲就是一个替换功能
1.将#define删除,展开所有的宏定义
2.处理预处理指令#include ,将所包含的头文件内容插入到预处理指令的位置,该过程可能为递归,可能出现一个头文件里面包含另外一个头文件
3.删除所有注释
4.保留行号和文件名,便于编译结束后生成调试信息
5.处理条件编译指令
......
经过预编译处理过程后,会生成一个没有宏定义,没有预处理指令的后缀名为 .i 的中间文件
编译:
将C语言转化为汇编语言
其主要做的工作就是词法汇总,语法汇总,语义分析,符号汇总
重点讲一下符号汇总:将每一个源文件中所用到的全局变量和函数名等进行汇总,为汇编过程生成符号表做准备
汇编:
将汇编语言转为二进制指令(即机器指令)
生成符号表:
给符号汇总出来的符号相应的地址
最终生成后缀名为(.obj)的目标文件
1.2链接:
链接主要工作就是:整合段表和和符号表的合并和重定位
符号表的合并
经过编译过程后每一个源文件都有自己的符号表,就可能出现不同的符号表里的符号重名但相应地址不一样的情况,因为最终是要生成一个可执行文件,不可能出现一个符号对应两个地址,所以就需要进行符号表的合并和重定位,使每个符号都只有一个相对应的地址最终生成一个有效的符号表例如在main函数里面引用了Add函数,但是Add函数的定义在另外的源文件中在编译阶段就会生成两个关于Add的地址,但是最终只有一个是有效的,也就是函数定义相对于的地址
编译处理后的目标文件都是经过编译器依依生成出来的,各文件之间都是相互独立的存在,没有建立起相互之间的联系
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。
由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
2.运行环境
1. 程序必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序的载⼊必须由⼿⼯安排,也可能是通 过可执⾏代码置⼊只读内存来完成。
2. 程序的执⾏便开始。接着便调⽤main函数。
3. 开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使⽤静态(static)内存,存储于静态内存中的变量在程序的整个执⾏过程⼀直保留他们的值。
4. 终⽌程序。正常终⽌main函数;也有可能是意外终⽌。