文章目录
翻译环境和运⾏环境编译预处理编译词法分析语法分析语义分析 汇编 链接地址和空间分配符号决议重定位
翻译环境和运⾏环境
在c语言标准(ANSI C)中的任何⼀种实现中,存在两个不同的环境。
翻译环境:在这个环境中将人写的文本代码翻译成机器“看得懂”的,可以直接执行的二进制代码运行环境:机器执行代码的环境编译和链接就是翻译环境的两个大过程
一个或多个源文件单独通过经过编译后生成一个或多个目标文件,多个目标文件经过链接库【链接库是指运⾏时库(它是⽀持程序运⾏的基本函数集合)或者第三⽅库】链接在一起生成可执行文件,可执行文件被机器直接指行的得出结果
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
编译
编译分为预处理(预编译),编译,汇编三个过程
预处理
预处理阶段主要是执行预处理指令,例如头文件的包含,替换#define定义的代码,宏等
具体的工作:
将所有的 #define 删除,并展开所有的宏定义。处理所有的条件编译指令处理#include ,将包含的头⽂件的内容插⼊到该#include的位置删除所有的注释添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等保留所有的#pragma的编译器指令,编译器后续会使⽤。预处理后的代码还是文本代码
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
编译
编译过程就是将预处理后的⽂件进⾏词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件。
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
词法分析
扫描器将代码拆分为一个个的特殊记号(关键字,标识符,特殊字符,运算符等),方便编译器解析
词法分析时还会将拆分后的符号汇总,方便链接的进行
例
a[i] = i * (2 + j);
该行代码会被扫描器分成11个记号,分别为:
a , [ , i, ] , = , * , ( , 2 , + , j , )
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
语法分析
语法分析器,将对扫描产⽣的记号进⾏语法分析,从⽽产⽣语法树
这些语法树是以表达式为节点的树
例
a[i] = i * (2 + j);
该代码拆分的词法形成的语法数为
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
语义分析
语义分析器来完成语义分析,即对表达式的语法层⾯分析。
编译器所能做的分析是语义的静态分析。
静态语义分析通常包括声明,类型的匹配,类型的转换等。
语义分析会报告错误的语法信息。
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
汇编
汇编器是将汇编代码转变成机器可执⾏的指令,每⼀个汇编语句⼏乎都对应⼀条机器指令。
汇编还会将编译的词法分析汇总的符号制成符号表【符号表就是多个符号+符号的地址所构成的表】,并将符号表传给链接库,方便链接
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
链接
链接的时候需要把⼀堆⽂件链接在⼀起⽣成可执⾏程序。
链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。
链接解决的是⼀个项⽬中多⽂件、多模块之间互相调⽤的问题
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
地址和空间分配
C语言的链接过程中,地址和空间分配是一个重要的环节,它涉及到程序的内存布局,即程序在内存中如何分布及其各自的地址范围。
以下是链接过程中地址和空间分配的一些关键点:
文本段(Text Segment):
文本段包含了程序的机器代码,即编译后的指令。在链接过程中,链接器会合并各个目标文件中的文本段,并确定唯一的入口点(通常是main
函数)。文本段的地址通常是可以预测的,因为它包含了程序的指令,这些指令在程序每次运行时都会被加载到内存的相同位置。 数据段(Data Segment):
数据段包含了程序中声明的静态变量和全局变量。在链接过程中,链接器会合并各个目标文件中的数据段,并为其分配连续的内存空间。数据段的地址在程序运行时是固定的,因为这些变量在程序开始执行时存在于内存中。堆(Heap):
堆是程序在运行时动态分配内存的区域。在C语言中,使用malloc
、calloc
、realloc
等函数动态分配的内存都位于堆上。-堆的地址空间在程序运行时是连续的,但操作系统会跟踪这些内存块的使用情况,以便于后续的释放和再分配。
栈(Stack):
栈是用于存储函数调用时的局部变量和返回地址等信息的区域。每个函数调用都会有自己的栈帧,用于存储局部变量和返回信息。栈的地址空间是向内生长的,新的栈帧会覆盖旧的栈帧。重定位表(Relocation Table):
重定位表记录了程序中需要重定位的符号和它们的新的地址。在链接过程中,链接器会生成重定位表,并在程序加载时由操作系统或加载器使用,以确保程序中的符号引用正确地指向其在内存中的位置。动态内存分配(Dynamic Memory Allocation):
如果程序使用了动态内存分配(如标准库中的malloc
等函数),那么这些内存将在程序运行时由操作系统动态地分配和释放。动态分配的内存通常位于堆上,但具体的地址是在运行时确定的。 一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
符号决议
符号决议是指链接器在将多个编译后的目标文件合并成一个可执行文件时,
确定每个符号(如函数名、变量名等)所对应的具体地址的过程。
这个过程确保了程序在运行时能够正确地找到并调用所需的函数和访问变量。
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
重定位
在C语言程序链接过程中,重定位是一种重要的步骤,它涉及将不同的目标文件中的符号(函数或变量)的地址合并到一个单独的可执行文件中。
当多个目标文件需要访问同一符号时,编译器无法确定哪个文件中的符号地址应该被使用,因此需要进行重定位。
重定位是程序能够正确访问外部函数和变量的关键。
C语言链接过程中的重定位主要分为静态链接和动态链接两种方式。
静态链接重定位:在静态链接过程中,链接器会在编译后的目标文件中将所有的符号(函数名、变量名等)和它们的地址进行绑定,并将这些地址填写到可执行文件中。
这个过程称为重定位。当可执行程序被操作系统加载到内存中时,由于所有的符号已经在链接过程中被重定位,因此程序可以直接使用这些符号,而不需要再进行地址绑定
动态链接重定位:静态链接的优点是运行效率高,因为它在编译和链接阶段就已经完成了所有的重定位工作。但是静态链接也有一些缺点,比如占用物理内存较多,且无法实现动态更新。
与静态链接不同,动态链接在编译阶段不会将符号和地址进行绑定,而是仅仅在可执行文件或动态库中记录一些必要的信息。
这些信息包括符号名称、符号类型以及符号在动态链接器中的地址。当程序运行时,操作系统会首先加载可执行文件和动态链接器到内存中,然后通过动态链接器来完成符号的绑定和重定位工作。
动态链接的优点是可以节省物理内存,因为它不需要将所有的库函数都包含在可执行文件中。
此外,动态链接还可以实现动态更新,例如在程序运行过程中可以动态地加载或卸载库函数。
但是动态链接也有一个缺点,就是程序的运行效率相对较低,因为它需要在运行时进行符号绑定和重定位。
一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一一
以上就是全部内容了,如果对你有帮助的话可以点个赞,支持一下