目录
在这里我们稍微了解一下我们平时写的C语言代码是如何通过编译器的一些操作最终成为可执行程序的。
1.翻译环境和执行环境
在ANSIC的任何实现中都存在两种环境:翻译环境和执行环境。
翻译环境:在这个环境中将源程序中的C语言代码翻译成机器代码。
执行环境:程序执行所需要的的环境,可执行程序在这个环境中运行实际代码代码。
如图:
2.编译和链接
C语言源代码(.c为后缀的文件通过编译器编译形成目标文件(在VS2019中是.obj为后缀的文件,在Linux中是以.o为后缀的文件),然后通过链接器链接在一起形成可执行程序。(在VS2019中是.exe为后缀的文件,在Linux中是a.out文件)。然后通过操作系统或者人手动导入到内存中,执行可执行程序。
如图:
组成程序的每个源文件都会通过编译器编译成一个目标文件,链接器将多个目标文件和链接库链接捆绑成一个可执行程序。
2.1编译
程序的编译分为预编译(预处理),编译和汇编。
预编译阶段主要完成的是:#define 定义的符号的替换和删除,头文件的包含和注释的删除等。
编译阶段主要是:将C语言代码转换为汇编代码,进行符号汇总,进行语法分析,词法分析和语义分析等。
汇编阶段主要是:将汇编代码转换为二进制的机器指令和形成符号表。
符号汇总都会汇总哪些符号呢?比如:全局的符号,比如:全局变量名,全局的函数名等符号。
形成的符号表是怎么样的呢?符号表存储着全局的符号和符号的地址。
2.2链接
链接阶段主要:进行段表的合并,符号表的合并和符号表的重定位。
二进制的机器指令可能是按照某种格式存储的,这时候将不同的目标文件和链接库合并到一起就需要将它们的机器码存储到同一个段表中,符号表相同的内容也要进行合并。
2.3程序的执行
程序在运行环境中执行程序:
1.程序必须载入内存,这一步是由操作系统完成的,如果没有操作系统就需要手动完成,也可能是通过可执行代码置入只读内存完成的。
2.程序的执行便开始了,接着调用main函数。
3.开始执行程序代码,这个时候程序会调用一个运行时堆栈,存储函数的局部变量和函数的返回值。程序同时也存在静态内存,存储于静态内存中的变量在程序运行的整个期间都会被保留。
4.程序结束,可能是正常结束也可能是异常结束。