计算机程序的编译和链接

9efbcbc3d25747719da38c01b3fa9b4f.gif

 c语言中的小小白-CSDN博客c语言中的小小白关注算法,c++,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm=1001.2014.3001.5343

给大家分享一句我很喜欢我话:

知不足而奋进,望远山而前行!!!

铁铁们,成功的路上必然是孤独且艰难的,但是我们不可以放弃,远山就在前方,但我们能力仍然不足,所有我们更要奋进前行!!!

今天我们更新了编译和链接内容,

🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝

前言:

在日常的应用程序开发过程中,我们很少需要关注软件的编译和连接过程,特别是对于常用的集成开发环境visual studio,它将编译和链接的过程封装起来,一步完成,称为“构建”。
     但是在这样的开发过程中,我们往往依赖于集成开发环境的强大,而忽略了软件的运行机制和机理,导致对程序中的很多莫名其妙的错误无从下手,程序运行时的性能瓶颈分析也让我们束手无策,如果我们能够深入了解软件运行背后的机理以及支撑软件运行的各种平台和工具,那么解决这些问题相对来说就比较容易了。接下来让我们一起了解软件编译与链接的过程。

一、预处理

  预处理过程主要处理那些源代码文件中的以“#”开始的预编译指令。比如”#include”,”#define”等,主要处理规则如下:
(1)将所有的”#define”删除,并且展开所有的宏定义。因为宏定义是直接展开的,所以我们在定义运算符相关宏时,切记要带上括号,避免导致歧义。
(2)处理所有的条件预编译指令,比如“#if“,”#ifdef“,”#elif”,“#else”,”#endif”
(3)处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置,注意:这个过程时递归进行的,也就是说被包含的文件可能还包含其它文件。当程序项目较大时,由于头文件包含较多,会导致编译速度减慢,此时可以从头文件的包含着手解决,避免包含无用的头文件,以及重复包含问题。
(4)过滤所有的注释“//“和”/**/“中的内容
(5)添加行号和文件名标识,比如#2“hello.c“ 2,以便于编译时编译器产生调试用的行号信息,及用于编译时产生的编译错误和编译警告时显示行号。
(6)保留所有的#pragma编译指令,程序编译时编译器需要使用到。
(7)处理预定义宏,在不同的编译器中,会有一些常用的预定义宏,比如__FILE__,__FUNCTION__,__LINE__。
(8)预处理不做任何语法检查,不仅是因为它不具备语法检查功能,也因
为预处理命令不属于 C/C++ 语句(这也是定义宏时不要加分号的原因),语法
检查是编译器要做的事情。

     通过以下命令可以对源文件进行预编译操作,编译后的文件扩展名是.ii。

二、编译

编译过程就是把与预处理完的文件进行一系列词法分析,语法分析,语义分析及优化后生成相应的汇编代码文件。可以通过以下命令进行编译(注意大写S):

gcc –S hello.i –o hello.s

  经过编译后的.s文件中是汇编代码,可以直接打开查看其内容:

究竟编译器做了什么?从最直观的角度来讲,编译器就是将高级语言翻译成机器语言的一个工具。比如用C/C++语言写的一个程序,可以通过编译器将其翻译成计算机可以执行的指令以及数据,编译的过程一般分为六步:扫描(词法分析),语法分析,语义分析,源代码优化,代码生成和目标代码优化。整个过程如图所示:

2.1词法分析: 

 array[index] = (index + 5) * (2 + 7);
 

  词法分析产生的记号一般可以分为以下几类:关键字,常数,运算符,标识符。在识别记号的同时,扫描器也完成了其它工作:比如将标识符存放到符号表,将数字和字符串常量存放到文字表等,以备后续步骤使用。
     对于C/C++语言,走到词法分析这一步时,宏替换以及文件包含已经在预处理中处理完毕。

2.2语法分析:

 在语法分析的同时,很多运算符号的优先级和含义也被确定下来了。比如乘法表达式比加法表达式的优先级高。另外有些符号具有多重含义,比如 * 在C语言中可以表示乘法表达式,也可以表示指针取内容的表达式,所以语法分析阶段必须对这些内容进行区分。如果出现了表达式不合法,比如各种括号不匹配、表达式中缺少操作符等,编译器就会报告语法分析阶段的错误

2.3语义分析:

 语义分析是由语义分析器来对表示的语法层面进行的分析,但是它并不了解这个语句是否真正有意义。比如C/C++中对两个指针做乘法运算是没有意义的,但是这个语句在语法上是合法的;比如同样一个指针和浮点数做乘法运算是否合法等。编译器所能分析的语义是静态语义,也就是编译期可以确定地语义,与之对应地动态语义就是只有在运行期才能确定的语义。
     静态语义通常包括声明和类型的匹配,类型的转换。比如当一个浮点型的表达式赋值给一个整型的表达式,这其中包含了一个浮点型到整型的准换过程,语义分析就负责完成这个步骤。比如将浮点数赋值给指针时,语义分析会发现这个类型不匹配(整型时可以赋值给指针的),编译器就会报错。
     动态语义一般指在运行期出现的语义相关的问题,比如将0作为除数是一个运行期语义错误。
     经过语义分析阶段后,整个语法树的表达式都被标上了类型,如果有些类型需要做隐式转换,语义分析程序会在语法树中插入相应的转换节点。上秒描述的语法树在经过语义分析阶段后变化如图所示:

2.4中间语言的生成:

现代的编译器有着很多层次的优化,往往在源代码级别会有一个优化过程。这里所描述的源码级优化器在不同编译器中可能会有不同的定义或者一些其它差异。源代码优化器会在源码级别进行优化,在上例中,我们可以发现,(2+7)这个表达式可以被优化掉,因为它的值在编译期就可以确定,优化后的语法树为:


     我们看到(2+7)这个表达式被直接优化成9。由于直接在语法树上进行优化比较困难,因此源代码优化器往往先将整个语法树转换成中间代码,它是语法树的顺序表示,已经非常接近目标代码了。但是中间代码一般跟目标机器和运行时的环境是无关的,比如不包含数据的大小,变量的地址和寄存器的名称等等。中间代码在不同的编译器中有着不同的形式,此处不再详细介绍。
     中间代码使得编译器可以被分为前端和后端:前端负责产生机器无关的中间代码,后端负责将中间代码转换成目标机器代码。这样对于一些跨平台的编译器而言,它们可以针对不同的平台使用同一个前端和针对不同机器平台的数个后端。

2.5目标代码的优化:

 源代码级优化器产生中间代码标志着下面的过程都是由编译器后端来完成的:代码生成器和目标代码优化器。
     代码生成器将中间代码转换为与机器相关的目标机器代码,这个过程依赖于目标机器的结构,因为不同机器的字长,寄存器,整数数据类型,浮点数数据类型都不一样(很简单的例子:32位操作系统和64位操作系统,指针变量所占字节数分别为4字节和8字节)。比如常用的GCC编译器就几乎支持所有的CPU平台,当然这也导致它的指令生成过程更为复杂。
     最后目标代码优化器对转换后的目标代码进行优化,比如选择合适的寻址方式,位移来代替乘法运算,删除多余的指令等。
     经过了词法分析,语法分析,语义分析,源代码优化,目标代码生成和目标代码优化,编译器经过这么多步骤,终于将源代码编译成目标代码。但是上述目标代码中index和array的地址还没有确定,如果现在把目标代码使用汇编器编译成真正能够在机器上执行的指令,那么index和array的地址是从哪里来的呢,如果它们定义跟上述源码在同一个编译单元内,那么编译器可以为它们分配空间,确定地址,但是如果index和array是定义在其它的程序模块中呢?
     事实上,定义其它模块的全局变量和函数在最终运行时的绝对地址都要在最终链接时才能确定。所以现代的编译器可以将一个源代码文件编译成一个未链接的目标文件(比如gcc中通过gcc –c hello.c 会生成hello.o文件),然后由连接器将这些目标文件链接起来形成最终的可执行文件(gcc hello.o)。

三、链接

把每个源代码模块独立地编译,然后按照需要将它们进行“组装”,这个组装地过程就是链接。链接地主要内容就是把各个模块之间相互引用地部分(包括函数和变量)都处理好,使得各个模块之间能够正确地衔接。
     从原理上讲,链接地工作无非就是把一些指令对其他符号地址地引用加以修饰,链接主要包括了地址和空间分配,符号决议和重定位这些步骤,
     举一个简单的例子:比如我们在模块main.c中使用另一个模块func.c中的函数foo(),我们在main.c模块中每一处调用foo函数的时候都必须确切知道foo的函数地址,但是由于每个模块都是单独编译的,在编译器编译main.c的时候它并不知道foo函数的地址(但是由于编译的预处理阶段,是将头文件全部替换的,因此编译单独编译main模块是没有问题的),所以暂时把这些调用foo的指令的目标地址搁置,等待最后链接的时候由连接器去将这些指令的目标地址进行修正。

Mov1 $0x2a, var

 这条指令就是给这个var变量赋值0x2a,由于比在编译目标文件B的时候,编译器并不知道变量var的目标地址,所以在这种情况下,编译器将这条mov指令的目标地址设为0,等待链接器在将目标文件A和B链接起来的时候再将其修正。假设A和B链接后,变量var的地址确定下来为0x100,那么链接起会把这个指令的目标地址修改成0x100。这个地址修正的过程叫做重定位,每个要被修正的地方叫一个重定位入口(在编译过程中报错,找不到函数的入口,那就是因为在链接时找不到该函数的地址)。

四、总结:

本期我们讲了关于计算机程序的编译与链接,希望对大家有所帮助!

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

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

相关文章

下载网页上的在线视频 网络视频 视频插件下载

只需要在浏览器上安装一个插件,就可以下载大部分的视频文件,几秒到一两个小时的视频,基本都不是问题。详细解决如下: 0、因为工作需要,需要获取某网站上的宣传视频,我像往常一样,查看视频的url…

Schemdraw小白从入门到放弃---原理工具书

文章目录 序版本最简单的例子一、总体思路二、元件2.1 color习题 2.2 label2.3 length 三、元件的连接3.1 延续性习题 3.2 方向习题 3.3 接线点习题3.3.1 默认激活anchor与沉默anchor3.3.2 切换鼠标焦点机制3.3.2.1 at函数规定元件的start接在哪个anchor上3.3.2.2 to函数规定元…

重磅!一起做个淘宝的简易布局!(超详细)

你好,我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端程序媛。 因为之前的学习内容,今天,我们可以来综合运用一下标签和 CSS 样式,做一个简易的淘宝网页大体布局了,如图。 咱们今天要做成这样子! 里面…

4.1 RK3399项目开发实录-案例开发之MIPI 摄像头开发(wulianjishu666)

嵌入式从零到项目开发全套例程资料 链接:https://pan.baidu.com/s/1ksCQN__jD8ZrJhw8sWzhwQ?pwdvvfz 3.2. MIPI 摄像头 带有 MIPI CSI 接口的 RK3399 板子都添加了双 MIPI 摄像头 OV13850 的支持,应用中也添加了摄像头的例子。下面介绍一下相关配置。…

蓝桥杯(2):python基础算法【上】

时间复杂度、枚举、模拟、递归、进制转换、前缀和、差分、离散化 1 时间复杂度 重要是看循环,一共运行了几次 1.1 简单代码看循环 #时间复杂度1 n int(input()) for i in range(1,n1):for j in range(0,i):pass ###时间复杂度:123....nn(1n)/2 所以…

2024 ccfcsp认证打卡 2023 09 02 坐标变换(其二)

202309-2 坐标变换(其二) 题解1题解2区别第一种算法(使用ArrayList存储操作序列):数据结构:操作序列处理: 第二种算法(使用两个数组存储累积结果):数据结构&a…

Unable to authenticate, need: BASIC realm=“Sonatype Nexus Repository Manager“

问题 使用公司的私有源,执行 npm i 的时候突然报错了: 解决 执行命令 npm config list找到 .npmrc 去掉对应的这一行即可,或者使用 nrm 工具执行 nrm del xxx 删掉私有源,然后在添加私有源也可。可以参考我这篇:使…

(一)基于IDEA的JAVA基础8

使用多重if选择结构 多个if条件进行判断: 语法: if(条件1){ 执行语句1; }else if(条件2){ 执行语句2; }else if(条件3){ 执行语句3; }else if (条件4)…… 流程图: 我们来写个好玩的,对暗号: public class Test01 { …

五分钟快速搭建个人游戏网站(1Panel)

五分钟快速搭建个人游戏网站(1Panel) 环境要求:主流 Linux 发行版本(基于 Debian / RedHat,包括国产操作系统); 如果是Windows OS的可以通过WSL来实现安装。 1 介绍 1Panel 是一个基于 Web 的 L…

结构体讲解

目录 一.结构体类型的声明 (1)结构体的声明 (2)结构体的创建和初始化 (3)匿名结构体 (4)结构体的自引用 二.结构体内存对齐 (1)对齐规则 (2)为什么存在内存对齐? (3)结构体传参 三.结构体实现位段 (1)什么是位段 (2)位段的内存分配 (3)位段的跨平…

电脑桌面便签,怎么在电脑桌面上设置便签

在数字化时代,电脑已成为我们日常生活不可或缺的一部分。在我们使用电脑进行各种工作和学习的过程中,经常会遇到需要记录临时信息或提醒自己的情况。这时,设置便签在电脑桌面上就成为了一种非常便捷的方法。那么有一个问题,电脑桌…

2.8、下拉刷新与上拉加载

页面的下拉刷新与上拉加载功能在移动应用中十分常见,例如,新闻页面的内容刷新和加载。这两种操作的原理都是通过响应用户的触摸事件,在顶部或者底部显示一个刷新或加载视图,完成后再将此视图隐藏。 实现思路 以下拉刷新为例,其实现主要分成三步: 监听手指按下事件,记录…

每天学点儿python(1)---print,input和注释

print函数 print语法格式 print(*objects, sep , end\n, filesys.stdout) sep参数默认为 一个空格 end(输出末尾)参数默认为 回车换行 file默认为 标准输出(一般指屏幕) 所以,如果想输出各个字段不用空格隔开&a…

vue3+threejs新手从零开发卡牌游戏(七):创建卡组

在开始前先优化下之前的代码: 在之前hand/p1.vue中为了定位 utils文件夹下新建common.ts,将一些公用方法提取出来放在这里: 在game/Cards.ts中,我们调整下卡牌的厚度,由原来的0.02改为0.005,原因是之前的…

【Ucore操作系统】4. 地址空间

文章目录 【 0. 引言 】背景本章任务 【 1. C 中的动态内存分配 】1.1 C语言的内存分配1.2 kalloc 中的动态内存分配 【 2. 地址空间 】2.1 虚拟地址和地址空间2.1.1 地址虚拟化出现之前2.1.2 加一层抽象加强内存管理2.1.3 增加硬件加速虚实地址转换 2.2 分段内存管理2.2.1 等量…

Docker专题-04 Nginx部署

Docker专题-04 Nginx部署 注: 本教程由羞涩梦整理同步发布,本人技术分享站点:blog.hukanfa.com 转发本文请备注原文链接,本文内容整理日期:2024-03-21 csdn 博客名称:五维空间-影子,欢迎关注…

javaSwing扫雷游戏

一、介绍 1.1 背景 在1964年 有一个叫“方 块”的游戏,这是扫雷最原始的版本。后来,这个游戏被改成了另一种游戏,叫做“Rlogic”。在这个游戏中,玩家扮演了一名军队的军人,接受了一项艰难的任务:为指挥中…

家政服务管理平台设计与实现|SpringBoot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW,文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java,…

【c++】类和对象(三)构造函数和析构函数

🔥个人主页:Quitecoder 🔥专栏:c笔记仓 朋友们大家好,本篇文章我们带来类和对象重要的部分,构造函数和析构函数 目录 1.类的6个默认成员函数2.构造函数2.1构造函数其他特性 3.构析函数3.1特性:…

sql——对于行列转换相关的操作

目录 一、lead、lag 函数 二、wm_concat 函数 三、pivot 函数 四、判断函数 遇到需要进行行列转换的数据处理需求,以 oracle 自带的表作为例子复习一下: 一、lead、lag 函数 需要行列转换的表: select deptno,count(empno) emp_num from…