C语言-程序环境 #预处理 #编译 #汇编 #链接 #执行环境

news2024/11/14 3:32:45

文章目录

前言

一、程序的环境翻译和执行环境

二、翻译环境

(一)、整体把握

(一)、编译

1、预处理(预编译)

2、编译

a、词法分析

b、语法分析

c、语义分析

d、符号汇总

3、汇编

(二)、链接

三、运行环境

总结​​​​​​​


前言

路漫漫其修远兮,吾将上下而求索;

PS:本文参考了《程序员的自我修养》,致敬大佬们!


一、程序的环境翻译和执行环境

在ANSI C(标准C)的任何一种实现中,存在两个不同的环境:环境翻译、执行环境

  • 翻译环境:在这个环境中源代码被转换为可执行的机器指令
  • 执行环境:用于实际执行代码

二、翻译环境

(一)、整体把握

在一个工程中会有很多的 .c 文件;

为什么在一个工程中会有多个 .c 文件?

  • 在一个开发组中,每个程序都要自己写自己的代码,倘若一堆程序员均将代码写入一个 .c 文件中,可以想象这是非常难以协同的;所以在一个工程之中,大家均是分模块去写的,故而在一个工程中必然会有多个 .c 文件;

编译器是如何处理多个 .c 文件生成可执行程序的呢?

  • 每一个 .c 文件被称为源文件每个源文件均会单独经过编译器处理生成自己相应的目标文件;然后多个目标文件 加上 链接库 经过链接器的处理最终会生成可执行程序;如下图过程所示:

  • 组成一个程序的每个源文件均会单独通过编译过程分别形成自己所对应目标代码(object code).
  • 每个目标文件由链接器(linker) 将和链接库 捆绑在一起,形成一个单一而完整的可执行程序
  • 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索到程序员个人的程序库,将其需要的函数也链接到程序中;

注:上图中所有的源文件均是由同一个编译器处理成目标文件的;(每个源文件均会单独通过编译器处理生成目标文件);

头文件是如何被处理的呢?

  • 头文件会先合并到源文件中(此合并的意思为拷贝,即将头文件中的内容拷贝放入源文件之中),然后再进行上图所示 的流程;

什么是链接库?

  • 我们在写代码的时候,倘若使用了库函数就得包含对应的头文件,例如 使用了库函数 printf 就得包含其对应的头文件  <stdio.h> ;而所包含的头文件,其涉及到依赖的库也是一个由库文件的提供,只有当你将其编译进你的程序之中,才可以使用此库函数。
  • 故,链接库同理;库函数所依赖的东西会在链接库中提供,然后再将链接库与目标文件链接到最终的可执行程序之中于是乎其整体(使用了库函数的程序)便可以使用此库函数了;

在整个程序生成可执行程序中会遇到两个工具 : 编译器、链接器

编译器、链接器的工具是什么呢?

  • 在VS底下编译器用的是 cl.exe ,链接器用的是 link.exe

2、细节深入

在整体把握中能明显感受到,多个源文件会由同一个编译器单独处理生成目标文件,而链接器会将这些生成的目标文件与链接库一起生成可执行程序;

在编译的过程中,还包含着三个过程:预处理(预编译)、编译、汇编

(一)、编译

1、预处理(预编译)

在预处理阶段,源文件会被处理成 .i 为后缀的文件; 

汇编过程的指令如下:

 gcc  test.c  -E  -o  test.i

       gcc  -test.c      是让gcc编译器编译源文件test.c

       -E                    代表着让gcc编译器将预处理阶段处理完便停止

       -o                    (output)代表着指定一个输出 

       -o test.i       就是将gcc 编译器处理完预处理阶段的数据存放到文件test.i 之中

预处理阶段主要处理的是那些源文件中# 开始的预编译指令;比如:#include #define ,

  • 处理#include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置;这个过程是递归进行的,也就是说被包含的头文件也有可能会包含其他文件;
  • 将#define 定义的标识符常量给替换掉,并且删除所定义的符号
  • 删除注释(PS: 注释是给程序员看的,这些注释对于程序本身来说没什么用,于是乎在预处理阶段便删除了)
  • 处理所有的条件编译指令,例如: #if 、#ifdef、#elif、#else、#endif ;
  • 添加行号和文件名标识,方便后续编译器生成调试信息
  • 保留所有的 #pragma 的编译器指令,编译器后续会使用;

源文件经过预处理后会生成以 .i 为后缀名的文件,在 .i 文件中不再包含宏定义,因为宏已经被展开。并且包含的头文件都被插入到 .i 文件中。所以当我们无法知道宏定义或者头文件的包含是否正确的时候便可以查看预处理后的 .i 文件来确认

注:这些操作均是将.c 源文件处理成新的文件,然后在新文件上操作的; 

2、编译

编译的过程就是将预处理后的文件进行一系列的:词法分析、语法分析、语义分析、符号汇总,之后便会生成对应的汇编代码文件;

编译过程的命令如下:

gcc  test.i  -S  -o  test.s

  • gcc test.i  -S 让gcc编译器处理文件test.i 到编译阶段结束便不再处理
  • -o  test.s  将gcc编译器编译阶段产生的数据存放到文件test.s 之中

例子程序:array [index] = (index + 4 ) * (2 + 6 );

a、词法分析

首先源代码被输入到扫描器(Scanner),扫描器的任务就是进行简单的词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。在识别记号的同时,扫描器也完成了其他的工作,比如将标识符放在符号表,将数字、字符串常量存放在文字表等,以便后面的操作使用;

将上述的例子程序进行词法分析,进行扫描后便会得到16个记号,如下图:

记号类型
array标识符
[左方括号
index标识符
]右方括号
=赋值
(左圆括号
index标识符
+加号
4数字
)右圆括号
*乘号
(左圆括号
2数字
+加号
6数字
)右圆括号
b、语法分析

加下来的语法分析器(Grammar Parser)将对由扫描器产生的记号进行语法分析,从而产生语法树。整个分析过程采用了上下文无关语法(Context-free Grammer) 的分析手段。上下文无关语法即由语法分析器生成的语法树就是以表达式(Expression)为节点的树;二我们知道,C语言的一个语句是一个表达式,而复杂的语句是很多表达式的组合。

在上面的例子当中的语句就是一个由赋值表达式、加法表达式、乘法表达式、数组表达式、括号表达式而组成的复杂表达语句;经过语法分析之后便会得到如下图所示的语法树:

在上图中可以得知,整个语句被当作了一个赋值表达式;赋值表达式的左边有个下标表达式(数组表达式),其右边是一个乘法表达式;而在下标表达式(数组表达式)中又由两个符号表达式组成……

符号和数字是最小的表达式,它们不是由其他的表达式来组成的,所以它们又通常被作为整个语法树的叶节点;

在语法分析的同时,很多运算符号的优先级和含义也被确定下来;

另外有些符号具有多重含义,例如* ,在C语言中可以标识乘法表达式,也可以表示对指针取类容的表达式,所以在语法分析阶段必须对这些内容进行区分;倘若出现表达式不合法,比如各种括号不匹配、表达式中缺少操作符等,编译器均会报出语法分析阶段的错误

c、语义分析

由语义分析器(Semantic Analyzer)完成,语法分析仅仅是完成对此表达式语法层面的分析,并不会去了解这个语句是否真的具有意义。编译器能做的就是语义的静态分析。静态语义分析通常包含声明和类型匹配,类型的转换。

经过语义分析阶段以后,整个语法树的表达式便都被标识了类型倘若有些类型需要做隐式转换,语义分析程序便会在语法树中插入相应的转换节点

如下图所示(还是同一例子):

d、符号汇总

符号汇总会将代码中全局、静态的符号全部汇总出来;

注:局部变量是无需汇总的,因为局部变量只有当程序运行起来进入其相对应的作用域时才会创建,故而局部变量无需汇总;

经过词法分析、语法分析、语义分析、符号汇总,在文件test.s 中存放的为源文件中代码所对应的汇编代码

3、汇编

汇编器汇编代码转变成机器可执行的指令(二进制指令),每一个汇编语句几乎都对应着一条机器指令;转换的流程就是根据汇编指令与机器指令的对照表一一地进行翻译,无需做指令优化;

汇编的指令如下:

gcc  test.s  -c  -o  test.o

汇编过程中的具体细节:

首先是会形成符号表,然后再将汇编指令转换成二进制指令;

注:什么叫作形成符号表?所谓形成符号表就是将在上阶段(编译)中的符号汇总再关联地址形成个表;

符号表是怎么形成的呢?

  • 会为对应的符号找补一个地址形成一个表格;(下图中的地址为举例而随便给的)

符号表有什么用?

  • 在链接阶段会使用到符号表;

(二)、链接

  • 合成段表
  • 符号表的合并和符号表的重定位

源文件经过预处理、编译、汇编 会形成 以 .o 为后缀的文件,即目标文件,此文件是有具体格式的,在Linux 环境下有一种 elf 格式(目标文件是中的内容是二进制形式的,在Linux 下按照elf 这种格式来组织其文件中的内容);

elf 格式会将目标文件分成一段一段的,每一段可以放置不同的数据(每个段均会放置某一种属性的数据);

假设此处有两个目标文件,即 test.o 与 add.o 

那么在此链接过程当中,会将test.o 与 add.o(多个目标文件)+ 链接库 进行链接,输出一个可执行程序;(在 Linux 环境下的可执行程序也是 elf 格式);

将elf 格式的文件合并,即在一个采用 elf 格式的文件中,不同的段中会放格子相同类型的数据,再将不同 elf 格式的文件其相同的段合并起来而形成一个文件;这个过程便是合并段表,如下图所示:

简单来说就是,在Linux 下,以 .o 为后缀的文件存在 elf 格式,即会将数据“分类”存在不同的段中(相同类型的数据会放在同一个段中),在链接阶段首先会将test.o 与 add.o 合并成一个文件,由于二者均为 elf 格式的文件,那么它们合并为一个文件的方式将存放着相同类型的段合并成一个段;

什么是符号表的合并与重定义?

(还是以上述所提例子为例)

符号表的合并

在符号表中Add 有两项,用哪一项呢?

经过仔细地发现 test.o 中的符号表中的 Add 的地址是无意义的,在 test.c 中只是进行了简单的声明,并无具体的实现,声明的作用也仅仅是让计算机知道有这么个函数,但是函数的具体实现及其真实的地址是不知道的,在 add.o 中Add 的地址是有意义的地址;故而在合并的过程中,会选择用add.o 中的 Add; 

如上图所示,在合并符号表的同时会对其地址进行筛选,即选择了有效的地址而形成了最终的符号表,这个筛选的动作便是符号表的重定位

如何确定该符号表是有效的?

  • 在 test.c 中仅存在Add 函数的声明而未有定义,故而在 test.o 文件所对应的符号表中其存在的Add 的地址是个无效地址;而至于此地址怎么是无效的,取决于编译器的实现(因为在汇编形成符号表的时候,编译器便认定了在test.s 中的Add 所找补的地址为无效地址,那么在合并符号表的时候便会认为该地址为无效地址);

符号表的重定位有什么用?

  • 符号表的作用是非常之大的;在链接期间,能否使用Add 函数完全取决于在符号表中Add 函数有没有对应其有效的地址,因为在合并之后,只有当Add 、main 均有自己的有效地址,故而我们可执行程序便会使用符号表去查找此函数的地址,便说明我们可以找到该函数(利用其有效的地址);但倘若 add.c 中没有Add 函数,但是在test.c 中仍然存在对于Add 函数的声明,那么经过预处理、编译、汇编所得到的符号表中Add 函数所对应的地址为无效地址,故而在合并符号表时 Add 函数仍任是对应着无效地址,当生成可执行程序的时候,便无法通过此无效地址而找到Add 函数,所以会报错,并且报的是链接性的错误

通过上面的讲述,似乎声明仅仅只是告诉计算机有这么个函数,并无其他实质性的作用;在合并符号表的时候,也会对符号表进行重定义;

可能你便会问了,倘若不对此函数进行声明,只要此函数的具体实现在这个工程中,可以使用该函数吗?

 

  • 当然可以,不对此函数进行声明,可以强行使用;会报警告,但是不影响执行;因为实质上可执行程序找Add 函数是在符号表中查找的

注:C++ 中的重载

在C++中,其编译器会根据其参数的个数、参数的类型等来确定重新产生的名字是什么……(有自己的一套规则),所以当你设计的函数的类型、参数个数不同时,重命名产生的名字也会不一样,那么在符号表中出现的名字也会有所差异;

编译期间会将符号进行汇总,而在汇编期间形成符号表,在链接阶段会合并符号表并进行重定位;这些操作就是为了在链接期间能够跨文件找到函数--> 在符号表中去查找(符号表的存在得以实现跨文件的使用);

三、运行环境

第一步将程序放入内存之后,接下来第二步程序便开始执行

而程序是如何开始执行的呢?

  • 首先是要找到main 函数的位置

main 函数被翻译成了二进制指令在内存中也存在属于main 函数自己的内存空间,故而首先会跳到main 函数存其对应二进制指令的地方开始执行(找到程序的入口);

程序的执行流程:

  • 程序必须载入内存中,在有操作系统的环境中,一般载入的这个操作是由操作系统完成的;而若在独立的环境中(即没有操作系统的环境中),程序的必须用手工载入,也可能是通过可执行代码置入只读内存中来完成;
  • 程序的执行便开始,接着便会调用 main 函数
  • 开始执行程序代码,这个时候将使用一个运行时堆栈(stack)每一个函数在调用的时候均会创建函数栈帧,此函数栈帧便是运行时堆栈),存储函数的局部变量和返回地址。程序同时也可以使用静态内存(静态区)存储于静态内存中的变量在程序的整个执行过程中一直会保留着他们的值;
  • 终止程序,正常终止main 函数;也有可能会是意外终止;(正常运行着的函数其程序结束了也便停止了;或者说程序发生了意外,程序崩溃了、内存泄露了、断电了……均属于意外终止)

注:只有将程序载入内存中,才能更好地去运行它;独立环境中的载入,例如:单片机中所讲的"烧板子,烫代码",便是将代码烫到板子上去(没有操作系统,需要借助于外部设备将程序放入内存之中);


总结

1、源文件生成可执行程序如若处理两步便是编译和链接。倘若是四步则是预处理、编译、汇编、链接。

2、预处理:主要处理的是那些源文件中# 开始的预编译指令;比如:#include #define ,

3、编译:词法分析、语法分析、语义分析、符号汇总

4、汇编:将汇编代码转换成机器指令,即二进制指令;

5、链接:

  • 合成段表
  • 符号表的合并和符号表的重定位

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2110815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

波导模式分析2 用于圆TE01模式高功率传输线的大型多模波导滤波器

摘要&#xff1a; 一种对于大型多模波导滤波器的设计方法&#xff0c;其能衰减掉&#xff08;deteriorate&#xff09;不想要的模式而不影响所需要的工作模式&#xff0c;被提出来抑制用于圆TE01模式高功率传输线的受限模式谐振。为了从TE10模式中分离出不期望的模式&#xff…

【蓝桥杯嵌入式(二)Led、Key、Lcd】

蓝桥杯嵌入式&#xff08;二&#xff09;Led、Key、Lcd 五、Led模块1.原理图配置2. 知识点3.底层代码 六、Key模块1.原理图配置2.知识点3.底层代码底层代码&#xff08;四⾏代码版本&#xff09;底层代码&#xff08;状态机版本&#xff09; 七、LCD模块1.原理图配置2.知识点底…

文章改写工具,帮你进行文章修改润色提升文章质量

在文字的世界里&#xff0c;每一篇文章都是创作者心灵的结晶。然而&#xff0c;即使是经验丰富的作家&#xff0c;也难免会在创作过程中遇到表达上的瓶颈。此时&#xff0c;文章改写工具便显得尤为重要&#xff0c;它以其独特的功能&#xff0c;对文章进行精准的修改与润色&…

机器学习算法那些事 | Plotly Express:一种简洁且强大的可视化神器

本文来源公众号“机器学习算法那些事”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Plotly Express&#xff1a;一种简洁且强大的可视化神器 Plotly Express 是 Python 交互式库 Plotly 的高级组件&#xff0c;受 Seaborn 和 g…

全国机器人大赛 Robocon 常州工学院团队首战国三

全国机器人大赛 Robocon 常州工学院团队首战国三 通宵7天7夜&#xff0c;常州工学院RC团队&#xff0c;首次闯入全国机器人大赛国赛&#xff0c;并成功得分&#xff01; 不同于老牌强队&#xff0c;常州工学院&#xff08;下面用"常工"代替&#xff09;的这只队伍&…

Java题集(由入门到精通)03

此系列文章收录大量Java经典代码题&#xff08;也可以算是leetcode刷题指南&#xff09;&#xff0c;希望可以与大家一起努力学好Java。3、2、1&#xff0c;请看&#xff01; 目录 1.创建学生成绩表 2.冒泡排序 3.模拟彩票中奖 4.杨辉三角 1.创建学生成绩表 输入n个学生的…

【学习笔记】SSL证书安全机制之证书撤销

前言&#xff1a;以往提到过&#xff0c;钓鱼网站会仿冒我们&#xff0c;如果我们的私钥泄露了&#xff0c;如果被不法分子得到了私钥&#xff0c;他们就能假装是我们网站。那现在&#xff0c;我们要做的是生成新私钥并申请新证书。问题来了&#xff0c;旧的证书亦然存在且有效…

胶条的回弹状态检测 仅需一台回弹测试仪

胶条的回弹状态检测之所以重要&#xff0c;主要基于以下几个方面的考虑&#xff1a; 一、保证产品功能性和密封性 密封效果&#xff1a;胶条作为密封元件&#xff0c;其回弹性能直接关系到密封效果的好坏。 耐久性&#xff1a;如果回弹性能不佳&#xff0c;容易出现永久性变形&…

8个txt自动化脚本,一定有你用得上的!Python如何读取txt文件数据!

这次和大家分享txt办公自动化&#xff0c;包括读取、对比、过滤、合并、转换格式、提取数据、统计词频、生成报告等。 分享一份Python学习大礼包&#xff08;激活码安装包、Python web开发&#xff0c;Python爬虫&#xff0c;Python数据分析&#xff0c;人工智能、自动化办公等…

GAMES101(0~1作业)

搭建虚拟机环境 安装Oracle VM VirtualBox虚拟机&#xff0c;安装虚拟硬盘&#xff0c;配置Linux Ubuntu-64 bit系统&#xff0c;启动虚拟机&#xff0c;发生冲突错误&#xff1a; 将Vmware虚拟设备取消挂起状态&#xff0c;关机确保 Hyper-V 完全关闭&#xff1a;bcdedit /se…

Pandas DataFrame的多级列索引导出到Excel时,如何避免空白行和列

我想将multi-header数据框保存为Excel文件。以下是示例代码&#xff1a; import pandas as pd import numpy as npheader pd.MultiIndex.from_product([[location1,location2],[S1,S2,S3]],names[loc,S])df pd.DataFrame(np.random.randn(5, 6), index[a,b,c,d,e], columnsh…

Python 中的 11 种经典时间序列预测方法(备忘单)

摘要: 本文演示了 11 种不同的经典时间序列预测方法,这些方法包括: 自回归(AR) 移动平均线 (MA) 自回归移动平均线 (ARMA) 自回归综合移动平均线 (ARIMA) 季节性自回归综合移动平均线 (SARIMA) 季节性自回归综合移动平均线与外生回归量... 本文演示了 11 种不同的经典时间序…

sheng的学习笔记-AI-半监督聚类

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 半监督学习&#xff1a;sheng的学习笔记-AI-半监督学习-CSDN博客 聚类&#xff1a;sheng的学习笔记-AI-聚类(Clustering)-CSDN博客 均值算法&#xff1a;sheng的学习笔记-AI-K均值算法_k均值算法怎么算迭代两次后的最大…

掌握Git分支管理策略:让团队协作更高效

在现代软件开发过程中&#xff0c;版本控制系统&#xff08;VCS&#xff09;是不可或缺的一部分。Git作为目前最流行的分布式版本控制系统之一&#xff0c;为开发者提供了强大的工具集来管理代码变更历史。然而&#xff0c;仅仅掌握Git的基本命令并不足以应对大型项目和团队协作…

当天审稿,当天上线,9月检索!

各领域CNKI知网普刊&#xff0c;最快一期预计下周送检&#xff0c;最快1天上线 领域广&#xff0c;计算机&#xff0c;社科&#xff0c;医学等各个方向都能收 包检索&#xff0c;可提供期刊部发票 知名出版社英文普刊 NO.1、Food Science and Nutrition Studies ISSN: 2573…

2024年全国大学生数学建模C题论文

C 题 农作物的种植策略 问题 1 &#xff1a;2024-2030 年农作物的最优种植方案 1.1 问题 1 的第一小问详细 该问题要求在假设未来农作物的预期销售量、种植成本、亩产量和销售价 格稳定的情况下&#xff0c;为乡村制定从 2024 年到 2030 年的农作物最优种植方案。特 别是要考虑…

骨传导耳机哪个牌子好用?精选五款黄金畅销骨传导机型测评

随着消费者对健康聆听方式的日益重视&#xff0c;骨传导耳机的市场需求持续高涨。众多耳机厂商在耳机的外观设计上倾注了大量心血&#xff0c;但在此过程中&#xff0c;部分品牌却忽视了产品的核心音质与佩戴舒适度&#xff0c;导致市场上涌现出一些外观时尚但内在品质不尽如人…

缓解父母焦虑!详细实测!这些免费AI可以成为孩子提高学习能力的得力助手!

近日&#xff0c;新版三年级英语教材冲上了热搜。家长纷纷表示&#xff0c;新版教材连26个英文字母都不教了&#xff0c;直接进入单词和文章教学。 ”娃都零基础&#xff0c;开学怎么跟得上&#xff1f;“ “这不是卷孩子&#xff0c;这是卷家长啊&#xff01;” 不仅是英语&…

【爱加密_云平台-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

[已更新问题二三matlab+python]2024数学建模国赛高教社杯C题:农作物的种植策略 思路代码文章助攻手把手保姆级

发布于9.6 10:00 有问题后续会修正!! 问题一代码展示: 问题二代码结果展示: 问题三代码展示: https://docs.qq.com/doc/DVVVlV0NmcnBDTlVJ问题一部分代码分享: #!/usr/bin/env python # coding: utf-8# In[15]:import pandas as pd# In[16]:# 读取Excel文件 file_path 附件2…