我们都知道,对于平常程序员写的代码,计算机是无法直接识别的,计算机所能识别的语言是二进制语言,而将我们的代码转化为计算机所能识别的二进制语言所经过的过程就是编译和链接。
具体是怎样实现这一过程呢,请看VCR~
目录
1.翻译环境和运行环境
在ANSI C(美国国家标准协会(ANSI)组成的一个委员会,X3J11,创立的 C 的一套标准)的任何一种实现中,存在两个不同的环境。
- 是翻译环境,在这个环境中源代码被转换为可执行的机器指令(⼆进制指令)。
- 是执行环境,它用于实际执行代码。
源文件通过翻译环境的编译链接后生成可执行程序,再在执行环境(或者说是运行环境)中运行,然后输出结果,过程大致如下:
接下来所说的编译和链接就都是在翻译环境中进行的。
1.1翻译环境
⼀个C语言的项目中可能有多个 .c 文件一起构建,对于多个 .c 文件生成可执行程序要经过一下步骤:
• 多个.c文件单独经过编译器,编译处理生成对应的目标文件。
注:在Windows环境下的目标文件的后缀是 .obj ,Linux环境下目标文件的后缀是 .o
• 多个目标文件和链接库一起经过链接器处理生成最终的可执行程序 .exe。
注:链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库。
大致过程如下:
1.2运行环境
1. 程序载入内存
在有操作系统的环境中:一般这个由操作系统完成。
在独立的环境中:程序的载入必须手工安排,也可能是通过可执行代码置入只读内存来完成。
2. 开始执行
调用main函数
3. 开始执行程序代码
这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4. 终止程序
正常终止main函数,也有可能是意外终止。
2.编译
编译(从.c到.obj(或.o))的整个过程就包含预处理(有的书也叫预编译)->编译->汇编,整体过程就是
源文件.c经预处理生成.i文件,再经编译生成.s,再经汇编生成.o
然后进入链接过程
2.1预处理(预编译)
从 .c到 .i
在gcc环境下生成.i文件命令行如下:
gcc -E test.c -o test.i
预处理阶段执行的任务是处理代码中以#开头的预处理指令
比如#include,#define等,大致内容如下 :
- 将所有的#define 删除,并展开所有的宏定义。
- 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif 。
- 处理#include预编译指令,展开头文件,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的。注意:如果头文件互相包含,那就会无限递归展开,这是不合理的写法。
- 删除所有的注释
- 添加行号和文件名标识,方便后续编译器生成调试信息等。
- 或保留所有的#pragma的编译器指令,编译器后续会使用。
大概就是带#的只有#pragma被保留
2.2编译
从 .i到 .s生成汇编代码
这个过程包括词法分析,语法分析,语义分析
命令如下:
gcc -S test.i -o test.s
比如说下面的一段代码:
arr[index] = (index+4)*(2+6);
对其进行编译
2.2.1词法分析
将源代码程序输入到扫描器,扫描器简单的进行词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。
上面的代码分割后的结果如下:
记号 | 类型 |
arr | 标识符 |
[ | 左方括号 |
index | 标识符 |
] | 右方括号 |
= | 赋值 |
( | 左圆括号 |
index | 标识符 |
+ | 加号 |
4 | 数字 |
) | 右圆括号 |
* | 乘号 |
( | 左圆括号 |
2 | 数字 |
+ | 加号 |
6 | 数字 |
) | 右圆括号 |
2.2.2语法分析
语法分析就是语法分析器对扫描器扫描产生的记号进行语法分析,然后产生语法树(以表达式为节点)
2.2.3语义分析
语义分析器进行语义分析,对表达式进行语法层面的分析,通常包括分析声明和类型的匹配,类型的转换等。在这个阶段编译器会报相关错误。
2.3汇编
从 .s到 .o
根据汇编指令和机器指令的对照表一一的进行翻译,不做指令优化
命令:
gcc -c test.s -o test.o
3.链接
链接就是把多个目标文件链接在一起生成可执行文件,解决一个项目中多文件,多模块相互调用的问题
过程主要包括:地址和空间分配,符号决议和重定位等
这里说一下重定位:
添加add.c放置Add函数的定义,在main.c里面声明外部函数Add并调用,由于每个文件都是单独编译的,所以 main函数调用Add时并不知道其地址在哪,所以会进行搁置,在链接的时候链接器根据符号Add在其他模块中找到函数地址,然后让main.c中引用到Add的指令进行修正,使main.c中Add的目标地址是Add的真实地址,这个修正地址的过程就叫重定位。
---------------------------------------------------------------------------------------------------------------------------------
c程序的编译链接过程其实非常复杂,前面的讲解只作初步了解
希望这篇文章可以对你有所帮助,走的时候记得留下小心心和关注哦~~~