本篇博客给大家带来的是代码从运行到生成可执行文件的流程和原理
🐟🐟文章专栏:C语言
🚀🚀若有问题评论区下讨论,我会及时回答
❤❤欢迎大家点赞、收藏、分享
你们的支持就是我创造的动力
今日思想:不能因为遇到了坏的事情,就总是悲观看人!!
Ⅰ、翻译环境和运行环境
在ANSIC的任何一种实现中,有两种不同的环境,他们有各自的作用
翻译环境:把源代码转换成可执行的二进制指令
运行环境:用于实际执行代码
Ⅱ、翻译环境=预编译+编译+汇编+链接
1、翻译环境
笼统的讲翻译环境是由编译和链接组成,但是编译又分成预处理(预编译)、编译、汇编三个过程。
编译: 通过cl.exe编译器把.c文件转换成.obj文件注意:在Windows环境下生成的是.obj文件,在Linux环境下生成.o文件
链接:把多个obj文件加上链接库经过链接器(link.exe)生成可执行文件
链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库。
2、预处理
预处理阶段:处理#开头的源文件
具体如下:
1.将所有的 #define 删除,并展开所有的宏定义。
2.处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
3.处理#include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。
4. 删除所有的注释
5.添加行号和文件名标识,方便后续编译器生成调试信息等。
6.保留所有的 #pragma 的编译器指令,编译器后续会使用。
3、编译
编译:将预处理生成的文件 进行一系列的词法分析、语法分析、语义分析及优化,然后生成相应的汇编文件
假设有下面的代码:
cpp
array[index] = (index+4)*(2+6);
3、1词法分析
语义分析:将源代码程序被输入扫描器,扫描器的任务就是简单的进行词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)。
将上面的代码进行词法分析可得16个记号:
3、2语法分析
语法分析:通过语法分析器将对扫描产生的记号进行语法分析,从而产⽣语法树。这些语法树是以表达式为节点的树。
语法树:
3、3语义分析
语义分析:由语义分析器来完成语义分析,即对表达式的语法层面分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。
4、 汇编
汇编:通过汇编器将汇编代码转变成机器可执⾏的指令(2进制的指令),每⼀个汇编语句几乎都对应⼀条机器指令。就是根据汇编指令和机器指令的对照表⼀的进行翻译,也不做指令优化**。**
5、链接
链接:把多个文件链接在一起生成可执行文件
过程:地址和空间分配、符号决议和重定位等步骤
目的:解决一个项目中多文件、多模块之间互相调用的问题
代码实例:
test.c文件代码
cpp
#include<stdio.h>
extern int Add(int, int);
extern int g_val;
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
add.c文件代码
cpp
int g_val = 2024;
int Add(int x,int y)
{
return x + y;
}
在链接阶段他们会合并各自的符号表:
合并为:
tste.c文件运行时会通过这个符号表来查找外部变量
Ⅲ、运行环境
1. 程序必须载入内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独立的环境中,程序 的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使⽤⼀个运行时堆栈(stack),存储函数的局部变量和返回 地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 ⼀直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
完!!