【C语言】编译和链接

一、编译环境和运行环境

在ANSI C的任何一种实现中,存在着两个不同的环境:

1、翻译环境:在翻译环境中,其会通过编译和链接两个大的步骤,其中编译又分为了预处理(这 个我们后面还会详细讲解),编译和汇编,然后将源代码转换为可执行的机器指令(二进制指 令),然后生成可执行文件。

2、运行环境:就是执行环境,在运行环境中会执行可执行程序,然后输出结果。

下面我们将在翻译环境和运行环境下具体是干啥的。

二、编译环境

编译环境就是将我们的源代码转换为可执行的机器指令,生成可执行程序,那么其是如何将源代码转换的呢?

翻译环境是由编译和链接两个大部分组成。然后其细分的话:预处理、编译、汇编三个过程。

1、编译

编译要完成的工作就是将我们的源代码转换为可执行的机器指令:

由于我们使用的编译器是一个集成开发的环境,其已经将编译的这个细节隐藏起来了,我们在运行程序的时候,其就帮我们把三个步骤都完成了。

我们可以在gcc的环境下看这个操作。

预处理:

预处理又叫预编译,在预处理的阶段,后缀为.c的文件将会被处理为.i的文件。

预处理主要做一下的事情:

1、将所有的#define删除,并展开所有的的宏定义。例如下面我们使用宏定义一个常量。

经过预处理后,那么第一个语句就会被删除,然后将这个文件的所有的N都替换成100。

2、处理所有的条件编译指令,如:#if、#ifdef、#elif、#else、#endif等等

3、处理#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置,

4、在预处理后,会删除那些注释,所以我们写的注释是不会影响程序的运行的。

5、添加行号和文件名标识。

6、或保留所有的#pragma的编译器指令,编译器后续会使⽤。

经过预处理后,.i文件不会再包含宏定义,当我们无法知道宏定义和包含的头文件是否正确的时候,此时我们可以查看预处理后的.i文件来确定。

编译 :

当我们预处理后,接下来就到了编译的阶段,编译就是将预处理的文件进行:词法分析、语法分析、语义分析和优化生成相应的汇编代码文件。

例如:

arr[index] = (index+4)* (2+6);

1、词法分析:

这个阶段会将源代码程序输入到扫描器,然后扫描器就会进行简单的词法分析,将代码中的字符分割成一系列的记号这样。(关键字、标识符、字面量、特殊字符等)。

如下:

在这个阶段会简单的标记每个记号,然后生成一个记号表,这个符合表在后面链接的地方还会再进行使用的。

2、语法分析:

到语法分析这个阶段后,其会将源代码放入语法分析器,然后对扫产生的记号进行语法分析,然后产生语法树。

如下:

其实到这个阶段,我们的代码大概的意思已经明确了。

在图上我们也可以看到这棵树的节点是一个又一个的表达式组成,如果在这个时候出现一些语法错误的话,可以很快的发现,然后程序就报错。

3、语义分析:

经过词法分析和语法分析后,就到语义分析,由语义分析器来完成语义分析,其实就表达式的具体语法层面的分析。

编译器可以做到的分析就是语义的静态分析、静态语义分析通常包括声明和类型的匹配,类型的转换等等。

如下:

汇编

经过预处理和编译阶段后,就来到汇编的阶段,在编译阶段的语义分析我们已经将源代码编译成了汇编代码,而在汇编阶段就是将翻译过来的汇编代码转换为计算机可以识别的机器指令。

在翻译的时候,每一个汇编语句几乎都对应一条机器指令,在翻译的时候不会再对代码进行啥优化的了,只会根据汇编指令和机器指令的对照表一一进行翻译,最终将汇编代码转换成了机器指令,然后生成一个.odj的目标文件。

2、链接

链接是一个非常复杂的过程,链接的时候需要将其一堆的文件链接在一块才可以生成可执行程序,链接过程主要是包括:地址和空间的分配,符号决议和重定位等这些步骤。

链接解决的是一个项目中多文件、多模块之间项目调用的问题。

如下:

我们当前的项目中有两个.c文件:

当我们实现好这两个文件后,直接运行看看:

可以看到其在我们的vs中是直接运行成功的,这是因为vs是一个集成开发的环境,其运行直接将链接的工作已经做了。

其整个过程大致为:首先先对这些.c文件进行编译,生成.odj文件,此时其相互之间是没影响的,我们需要在这个文件中使用另外一个文件的内容,那么此时就需要通过我们的链接来完成。

在链接过程中,需要使用到我们前面的词法分析时生成的符号表,将这些特殊符号记录下来,但是就是会复杂一点,其会导出符号表,未解决符号表和地址重定向表三个表。

下面为符号的类型:

1、全局符号:

由当前模块定义并能被其他模块引用的符号

2、外部符号:

由其他模块定义,并能被当前引用的全局符号

3、局部符号:

由当前模块定义和引用的本地符号。

那么在上面的情况:在add.c的文件中,那么add就是全局的符号,其拥有自己的地址,然后在这个文件中没有外部的符号。

text.c文件的话,add符号被当做了外部的符号,那么在这个文件中,其是不认识add这个符号的,那么我们为了其可以正常链接,那么我们就可以在text.c中使用extern关键字对add符号进行声明,那么此时链接器就知道,这个符号在其他的文件中是有定义。

然后此时我们给在这个文件的其实是一个假的地址,先让程序继续走下去,到最后我们会为了修正text.c文件中add符号的地址,使其正确,会对其进行重定位操作,其会计算每个定义的符号在虚拟地址空间的绝对地址,然后将可执行文件中的符号引用处修改为重定义后的地址。

然后text.c文件中使用这个add.c文件中的符号的地址就是正确的了。

这就是链接中的重定位。

三、运行环境

运行环境的要点如下:

1、程序首先必须载入到内存中去,在有操作系统的环境下,一般由操作系统完成,在独立的环境下,程序的载入必须由手工安排,也可能通过可执行代码置入只读内存来完成。

2、随后程序开始执行,其会直接调用main函数

3、开始执行程序的代码,这个时候程序就会使用一个运行时堆栈,存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储于静态内存中的变量在程序的整个执行过程一直会保留它们的值。

4、结束程序,正常结束main函数,不过还有可能是因为错误结束。

相关推荐
风中飘爻43 分钟前
JavaScript:BOM编程
开发语言·javascript·ecmascript
kyle~1 小时前
ROS2---std_msgs基础消息包
开发语言·python·机器人·ros·机器人操作系统
满怀10151 小时前
【NumPy科学计算引擎:从基础操作到高性能实践】
开发语言·python·numpy
我命由我123452 小时前
35.Java线程池(线程池概述、线程池的架构、线程池的种类与创建、线程池的底层原理、线程池的工作流程、线程池的拒绝策略、自定义线程池)
java·服务器·开发语言·jvm·后端·架构·java-ee
&zzz2 小时前
Python生成exe
开发语言·python
Chandler242 小时前
Go:方法
开发语言·c++·golang
CopyLower3 小时前
分布式ID生成方案的深度解析与Java实现
java·开发语言·分布式
随便@_@4 小时前
基于MATLAB/simulink的信号调制仿真--AM调制
开发语言·matlab·simulink·移动仿真
爱代码的小黄人4 小时前
深入解析系统频率响应:通过MATLAB模拟积分器对信号的稳态响应
开发语言·算法·matlab
vsropy4 小时前
matlab安装python API 出现Invalid version: ‘R2022a‘,
开发语言·python