互联网登录的网站名,深圳建站公司 方网站,wordpress表插件,企业网站 程序【C语言】 —— 编译和链接 一、编译环境和运行环境二、翻译环境2.1、 预处理2.2、 编译#xff08;1#xff09;词法分析#xff08;2#xff09;语法分析#xff08;3#xff09;语义分析 2.3、 汇编2.4、链接 三、运行环境 一、编译环境和运行环境 平时我们说写 C语言… 【C语言】 —— 编译和链接 一、编译环境和运行环境二、翻译环境2.1、 预处理2.2、 编译1词法分析2语法分析3语义分析 2.3、 汇编2.4、链接 三、运行环境 一、编译环境和运行环境 平时我们说写 C语言 代码写程序不难发现其实写出来的都是 t e s t test test. c c c、 t e s t test test. h h h 等源文件和头文件。我们直接打开他们是可以直接看懂的这也就说明了他们其实是文本文件。但是计算机是看不懂他们的计算机只能识别二进制指令无法对文件中的代码直接执行。这时就需要将 C语言 代码进行处理变成二进制的指令。 而将代码处理成二进制指令正是编译器需要做的事情。 在 ANCI C 的任何一种实现中存在两个不同的环境:
翻译环境在这个环境中源代码被转换成可执行的机器指令二进制指令执行环境它用于实际执行代码 二、翻译环境 那翻译环境是怎么将源代码转换为可执行的机器指令的呢这里我们就得展开讲解一下翻译环境所做的事情 其实翻译环境是由编译和链接两大过程组成而编译又可以分解成预处理有些书也叫预编译、编译、汇编三个过程。 一个C语言的项目中可能由多个 . c c c 文件一起构建那多个 . c c c 文件如何生成可执行程序呢
多个 . c c c 文件单独经过编译器编译处理生成对应的目标文件。在 W i n d o w s Windows Windows 环境下的目标文件的后缀是 .obj在 L i n u s Linus Linus环境下目标文件的后缀是 .o多个目标文件和链接库一起经过连接器处理生成最终的可执行程序。链接库是指运行时库它是支持程序运行的基本函数集合、C语言库函数或者第三方库。 这里需提一下我们所用的 V S 2022 VS2022 VS2022 是一种集成开发环境包括编辑器、编译器、链接器、调试器。而 c l cl cl. e x e exe exe 是其编译器 l i n k link link. e x e exe exe 是其链接器 我们可以在 L i n u s Linus Linus 服务器、 g c c gcc gcc 的环境下对链接和编译各个阶段进行观察
2.1、 预处理 在预处理阶段源文件和头文件会被处理成 .i 为后缀的文件。 在 g c c gcc gcc 中将 .c 文件处理成 .h 文件命令如下
gcc -E test.c -o test.i预处理阶段主要处理那些源文件中 # 开始的编译指令。比如# i n c l u d e include include# d e f i n e define define处理的规则如下
将所有的 # d e f i n e define define 删除展开所有的宏定义处理所有的条件编译指令如#if、#ifdef、#elif、#else、#endif处理 # i n c l u d e include include 预编译指令将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的也就是说被包含的头文件也可能包含其他文件。删除所有注释添加行号和文件名标识方便后续编译器生成调试信息等或保留所有的 # p r a g m a pragma pragma 的编译器指令编译器后续会使用 经过预处理后的 . i .i .i 文件不再包含宏定义因为宏已经被展开。并且包含的头文件都被插入到 .i 文件中。所以我们无法知道宏定义或者头文件是否包含正确的时候可以查看预处理后的 . i .i .i 文件来确认。 t e s t test test. c c c 文件 t e s t test test. i i i 文件 . i .i .i 文件只有输入指令生成后我们才能看到正常情况下生成中间文件编译器用完就删掉了。
2.2、 编译 编译过程就是将预处理后的文件进行一系列的词法分析、语法分析、语义分析及优化生成相应的汇编代码文件。
编译过程的命令如下
gcc -S test.i -o test.s编译过程最终会生成 .s 的文件它里面放的是汇编代码。其实编译阶段整体上就是将 C 代码转换成汇编代码 t e s t . s test.s test.s 文件 那编译过程具体是做了什么工作呢 他们分别是词法分析、语法分析、语义分析及优化 下面让我们一起简单了解 假设有下面的代码在进行编译时会时编译器怎么做呢
array[index] (index 4) * (2 6);1词法分析 将源代码程序输入扫描器扫描器的任务就是简单的进行词法分析把代码中的字符分割成一系列的记号关键字、标识符、字面量、特殊字符等。
上述代码进行词法分析后得到了 16 个记号
记号类型记号类型array标识符4数字[左方括号)右圆括号index标识符*乘号]右方括号(左圆括号赋值2数字(左圆括号加号index标识符6数字加号)右圆括号 2语法分析 接下来则是语法分析。语法分析器会对扫描产生的记号进行语法分析从而产生语法树。这些语法树是以表达式为节点的树 3语义分析 由语义分析器来完成语义分析即对表达式的语法层面分析。编译器所能做的分析是语义的静态分析。静态语义分析通常包括声明和类型的匹配。这个阶段会报告错误的语法信息 2.3、 汇编 汇编是指通过汇编器将汇编代码转变成机器可执行的指令每一个汇编语句几乎都对应一条机器指令。就是按照汇编指令和机器指令的对照表一一的进行翻译也不做指令优化 汇编的命令如下
gcc -c test.s -o test.o经过汇编处理后文件即为目标文件 . o b j .obj .obj / . o .o .o目标文件为二进制文件无法通过文本编辑器打开 2.4、链接 链接是一个复杂的过程链接的时候需要把一堆文件链接在一起才生成可执行程序。 链接的过程主要包括地址和空间分配符号决议和重定位等这些步骤。 链接主要解决的是一个项目中多个文件、多模块之间互相调用的问题。 比如现在有两个文件 t e s t . c test.c test.c 和 a d d . c add.c add.c t e s t . c test.c test.c 文件
#inclduestdio.h//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;int main()
{int a 10;int b 20;int c Add(10, 20);printf(%d\n, c);return 0;
}A d d . c Add.c Add.c 文件
int g_val 2024;int Add(int x, int y)
{return x y;
}为什么在 Add.c 中定义的文件在 test.c 文件中声明一下就可以使用呢 这里进行简单的了解 经过前面的学习我们知道每一个源文件 . c .c .c经过编译过程后都会生成自己的目标文件 . o .o .o / . o b j .obj .obj 在编译的过程中会对代码中的符号进行符号的汇总并形成相应的符号表符号表中会存储符号相对应的地址。在产生 t e s t . c test.c test.c 文件的符号表时遇到只有声明而未定义的符号 Add 和 g_val 时会暂时将其地址搁置。 到了编译过程编译器会将多个目标文件链接在一起目标文件的格式是一样的并且是分段的形式从而生成可执行程序可执行策划给你续最终也是如目标文件一样的分段形式 在合并过程中符号表也需要合并成一份。合并后的符号表每个符号只能有一份那 Add 和 g_val 符号自然用其有效地址的那一份这样符号表就完成了合并。通过 A d d Add Add 的地址就自然而然能找到 A d d Add Add 函数了。 而上述对地址的修正过程被叫做重定位 前面我们非常简洁的讲解了一个 C 的程序是如何编译和链接到最终生成可执行程序的过程其实很多内部的细节无法展开讲解。 比如目标文件的格式 e l f elf elf链接底层实现中的空间与地址分配符号解析和重定位等如果你有兴趣可以看 《程序员的自我修养》 一书来详细了解。
三、运行环境 运行环境实际上是非常复杂的我们这里简单了解了解
程序必须载入内存中。在有操作系统的环境中一般这个由操作系统完成。在独立的环境中程序的载入必须由手动安排单片机烧板子也可以通过可执行代码置入只读内存来完成。程序的执行便开始。接着便调用 m a i n main main 函数开始执行程序代码。这个时候程序将使用一个运行时堆栈函数栈帧存储函数的局部变量和返回地址。程序同时也可以使用静态static内存存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。终止程序。正常终止 m a i n main main 函数也有可能是意外终止。 好啦本期关于编译和链接的知识就介绍到这里啦希望本期博客能对你有所帮助。同时如果有错误的地方请多多指正让我们在C语言的学习路上一起进步