接下来,让我们对C语言编译与链接相关知识进行易于理解的探讨与学习。
一.翻译环境和运行环境
在ANSIC的任何⼀种实现中,存在两个不同的环境——翻译环境和运行环境。
翻译环境就是将代码变为机器可执行的机器指令。
运行环境就是让代码实际执行起来的环境。
二.翻译环境
翻译环境是将源代码转换为机器可执行程序的过程,包括编译和连接两个过程,其中编译又包括预处理(又叫预编译)、编译、汇编三个过程。
下面的图片或许可以让你对翻译环境有更多的理解:
从上面的图片中我们可以得出观察到关于翻译过程的很多小细节:
1.每个源文件(.c)分别进行各自的编译生成各自的目标文件(.obj)
2.很多个目标文件与链接库一起进入连接器生成一个可执行的程序(.exe)
注意:
.在Windows系统下目标文件为.obj,在linux系统下目标文件为.o
.链接库指运行时库(它是支持程序运行的基本函数集合)或者第三方库。
1.编译
编译实际上是将源代码转换为目标代码的过程,现在让我们一起来学习一下编译的各个过程分别发挥了什么作用。
编译器实际上是将编译分为了三个过程:
1.1预处理
预处理时,.c后缀的源文件会被处理成.i后缀的文件。
那么在这个过程中,具体会发生呢?
其实在预处理阶段,主要处理那些#开始的预编译指令,例如#define,#include:
1.删除所有#define 展开所有宏
2.删除所有条件编译指令,例如#if、#ifdef
3.将所有被包含的头文件插入到被引用的位置
4.删除所有的注释
5.保留#pragma编译器指令,方便后续使用
6.添加行号和文件名标识
1.2编译
编译会把.i后缀的文件处理成.s后缀的文件
编译的过程需要进行词法分析、语法分析、语义分析及优化三个过程,文件进入三个相应操作的机器完成相关的操作。
通俗来说,词法分析就是将输入到扫描器,扫描器将代码分割成一个又一个词来进行分析,如
d=(b*2)+(c-2)
那么词法分析器就将其分割:
d、=、(、 *、 )、+、(、 c、 -、2、)
语法分析器是将扫描产生的词进行语法分析,它是以表达式为节点形成语法树。
语义分析以及优化是由语义分析器来完成的,在这个阶段,语义分析器会对语法树进行分析,检查声明和类型的匹配、类型的转换。在这个阶段会报告错误的语法信息。
1.3汇编
汇编就是将.s后缀文件处理成.o后缀的文件。汇编是通过汇编器将汇编代码转变为机器可执行的程序指令。
2.链接
链接解决的是一个项目中多文件、多模块之间相互调用的问题。我们现在上手实操一下吧!
在main.c中,我们用extern声明了外部全局变量与外部函数。
在编译过程,main.c与add.c过程,两个源代码经过各自的编译,形成各自的目标文件;在编译过程,两个目标文件加上链接库形成一个可执行的程序。
我们在main.c文件中每次使用Add函数与b变量都需要知道他们的地址,但由于编译是各个源文件独立进行的,所以在编译main.c文件时我们无法得知函数与变量b的地址,所以我们暂且把他们的地址搁置。到了链接阶段,连接器根据引用的符号Add和b在其他模块中查找他们的地址,然后对main.c中所有使用了Add地址和b地址的指令进行修正,让他们的目标地址为真正的函数与b变量的地址。这个地址修正的过程又被叫做:重定位。
三.运行环境
1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排(如单片机烧录程序),也可能是通过可执行代码置入只读内存来完成。
2. 程序的执行便开始。接着便调用main函数。
3. 开始执行程序代码。这个时候程序将使用⼀个运行时堆栈(stack),存储函数的局部变量和返回地址(函数栈帧的创建与销毁)。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程 一直保留他们的值。
4. 终止程序。正常终止main函数;也有可能是意外终止。
好了,关于C语言编译与链接的知识我们就了解到这里,感谢收看,欢迎批评指正。