引入:
1、程序的翻译和执行环境
在ANSI C的任何一种实现中,存在两种不同的环境。
第一种是翻译环境,在这个环境中,源代码被转化为可执行的机器指令。
第二种是执行环境,它用于实际执行代码。
<1>翻译环境
组成一个程序的每个源文件通过编译过程分别转化为目标代码。(object code)。
(Windows环境下文件后缀为.obj,Linux环境下后缀为.o)
每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
链接器同时也会引入标准C函数库之中任何被该程序用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
简而言之,翻译环境就是:源文件->编译、链接->可执行文件这个过程中所处的环境。
<2>执行环境
可执行程序运行的环境。
2、编译本身也分为了几个阶段
<1>预编译:
预编译:gcc test.c -E(Linux)
1、头文件的包含。
#inlcude是一种预处理指令
2、#define定义符号的替换。
#define也是预处理指令
3、注释删除。
总结:预编译做文本操作。
<2>编译:
编译:gcc test.i -S(test.i是预处理产生的文件)(产生test.s)(Linux环境下)
把C语言代码翻译成了汇编代码
包含了:
1、语法分析
2、词法分析
3、语义分析
*4、符号汇总(汇总全局符号,函数名(自定义函数)啥的,还有main,局部符号不会汇总)
(出错就编不过去,报错)
<3>汇编:
汇编:gcc test.s -c(会生成test.o)(Linux环境下)
windows环境下目标文件是.obj
Linux环境下的目标文件是.o
目标文件是二进制的
将汇编指令翻译成二进制指令
形成符号表(将编译过程中汇总的符号形成一个表,一 一 对应)。
<4>链接:
把前几个文件和链接库放在一起
1、合并段表
2、符号表的合并和重命名(就是类似找到汇总的符号的有效地址,例如在源文件中声明了一个函数假设叫Func,如果有声明,有定义,在符号汇总时,会将两个Func符号都汇总,而由Func函数的声明收集而来的Func符号其实对应的是一个无效地址,而由Func函数实现部分收集而来的Func符号就会对应一个有效地址,在链接时,如果遇到那个对应无效地址的Func符号,就会去符号表中找是否有该符号的有效地址,若有,就合并,若没有,但是使用到了这个函数,就会报错,说该Func函数未定义)。
3、运行环境
程序执行的过程:
<1>程序必须载入内存中。在有操作系统的环境中,这个操作一般由操作系统完成。在独立的环境之中,程序的载入由手工安排,也可能是通过可执行代码置入可执行内存来完成。
<2>运行环境是程序一旦执行便开始,接着便调用main主函数。
<3>开始执行程序代码。这个时候将使用一个运行时堆栈(stack),储存函数的局部变量和返回地址。(函数栈帧的创建和销毁)程序同时也可以使用静态内存(static)存储于静态内存中的变量在程序的整个执行过程中一直保留它们的值。
<4>终止程序。正常终止main函数,也可能非正常终止。
水平有限,欢迎指正。