你真的懂Hello World!吗?(编译与链接,静态链接与动态链接)

💫Hello World!

  对于大家来说Hello World!应该是最熟悉不过的一句话,我们从Hello World!走进了计算机的世界,但是你真的了解Hello World!吗?你又思考过它背后蕴含的机理吗?他是怎么从代码变成程序的你真的思考过吗?
  今天本篇文章会对它的底层做最基本的讲解,后续博主会再次对它进行深入解析!!
  那么我们开始吧!!

💫被隐藏的部分——编译与链接

  对于visual studio这样的集成开发平台(编译,链接,调试集一体),要看到细节是很难的,所有本文将使用Linux的gcc编译器进行操作。
  一个源文件变成可执行程序需要经过以下几个步骤:
1.预处理(也叫预编译)  2.编译  3.汇编  4.链接
  每个步骤又包含有许多小步骤。

⭐️预处理

  预处理的工作主要有以下:
1.宏替换(删除所有的“#define”,展开所有的宏)
2.条件编译
3.头文件包含
4.去注释

  我们对以下程序做预处理:
在这里插入图片描述

  我们输入编译指令,查看预处理之后生成的.i文件
  输入指令gcc -E file.c -o file.i -Dversion1=1(与在文件内部#define version1 1作用相同)
  (-E指令表示进行程序翻译在预处理完成后停下来
  (-o指令表示命名,上面指令表示把经过预处理后的文件命名为file.I)
  (-D能够实现对程序进行动态裁剪,假如我们的version1,version2,version3分别表示程序的不同版本,那么通过条件编译就能控制程序不同版本的发布)
  我们截取file.i的一部分
在这里插入图片描述  我们可以看到,以上几个步骤实际发生了,这里需要提及一下头文件包含,实际上头文件包含就是把头文件里面的内容拷贝到了我们的文件里。

⭐️编译

  编译的过程就是对预处理后文件进行一系列的处理(词法分析,语法分析,语义分析,符号汇总以及优化过程)最后产生汇编代码的过程。
我们对一个最简单的表达式做以上处理:

int A=7+9

  词法分析简单来说就是记录符号的过程。

记号类型
A标识符
7数字
9数字

  语法分析是生成语法树的过程。
在这里插入图片描述

  语义分析就是对表达式做语义分析判断它是否有意义,语义又分为静态和动态语义,静态语义通常是声明和类型转换,动态语义指在程序运行出现的语义问题,例如除零错误。
语义分析后的语法树
在这里插入图片描述  我们在命令行输入
gcc -S file.i -o file.s
  (-S指令表示开始程序翻译,编译步骤结束后停下来)
  以下就是编译后的汇编代码
(注:我们不需要懂汇编代码,现在我们只需要知道有转汇编这个步骤)
在这里插入图片描述

⭐️汇编

  汇编就是将汇编代码变成机器可以执行的指令的过程,最后输出一个目标文件(可重定位二进制文件),汇编有一个重要的步骤——形成符号表这个需要我们注意。
  在命令行输入
gcc -c file.s -o file.o
(-c指令表示开始程序翻译,汇编步骤结束后停下来)
  打开.o文件
在这里插入图片描述  里面大部分都是乱码,但是我们可以看到ELF和.data这些符号,对。这个很重要。
  我们可以使用od指令打开它,但是并没有一点用,二进制我又看不懂。
在这里插入图片描述
  与其说它是一个二进制文件,但实际上他是一个ELF文件。它有重要的段表和符号表,就是基于这个文件才能进行后续的链接步骤。ELF文件类型很多(例如动态库文件,可执行文件,可重定位二进制文件都是ELF文件)。

  我们可以使用readelf工具读取ELF文件
命令行输入readelf -a file.o可以详细的查看ELF文件,包括ELF文件头,段表,符号表等等

文件头
在这里插入图片描述段表
包含段的信息,有address(段虚拟地址),offset(段偏移),flags(段标志)等等等一系列信息。
在这里插入图片描述符号表

在这里插入图片描述  细心的小伙伴可能发现符号表里面怎么有puts符号,懵逼了吧,其实这里是gcc搞的鬼,他把只打印一个字符串的printf自作聪明的换成了puts.

num:符号表数组下标
value:符号值
size:符号大小
type:符号类型
bind:绑定信息
ndx:在语言中是否使用
name:符号名

⭐️链接

  链接是重新翻译的最后一步,链接后就生成可执行程序。
  链接步骤中至关重要的就是段表的合并符号表的合并与重定位工作
  关于符号表
  通过上面的学习我们知道在编译过程中有对符号的汇总,汇编步骤形成符号表,最后链接对符号表进行合并与重定位,他是怎么实现的。
我们有以下程序:
在这里插入图片描述
在这里插入图片描述  我们就拿Add符号举一个例子。
  在编译时两个文件都对符号进行汇总,两个文件都有一个相同符号Add
  在汇编阶段形成符号表,对于Add.c文件形成的符号表会给Add符号一个实际地址,对于test.c文件形成的符号表编译器并不知道它的目标地址会把他的目标地址置为0.
  最后链接器将他们链接起来的时候会将地址修正,也就是重定位。
  分别查看三个文件的符号表

在这里插入图片描述在这里插入图片描述
  最后的可执行程序的符号表就不再展示,链接会链接库,最后的符号表有库文件的符号,使得可执行符号表很长,有兴趣的小伙伴可自行查看。
  关于段表
  我们可以使用objdump工具查看段
  命令行输入objdump -h test.o
在这里插入图片描述其中size表示段长,file off表示段的位置

  这里介绍一下常见且易于理解的段
   .text段(代码段):存放编译后的机器指令
   .data段(数据段):初始化的全局变量以及静态局部变量(如果初始化为0则将其放在.bss段)
  .bss段:未初始化的全局变量以及静态局部变量
  .rodata(只读数据段):只读变量,例如const变量,以及字符串常量
  .rel.text:重定位表,存储的重定位信息。
命令行输入objdump -x -s -d test.o
(-s表示把段内容以十六进制打印出来,-x表示反汇编)
在这里插入图片描述  我们看.data和.rodata段
  .data前四个从低到高是0x0a 0x00 0x00 0x00表示的就是global_init_var
  同理后四个字节是static_var
  .rodata的256400既是表示字符串常量%d
  命令行输入objdump -r test.o可查看重定位表
  以下是代码段重定位表
  offset表示重定位入口的偏移。
在这里插入图片描述

💫动态链接与静态链接

⭐️ 动态库与静态库

动态库静态库
Windows.dll.lib
Linux.so.a

  动态库用于动态链接,静态库用于静态链接
  动静态库的本质其实是目标文件的集合。
  在Linux中链接默认进行动态链接。
file查看文件类型,ldd查看库依赖
在这里插入图片描述在这里插入图片描述框出的部分是C运行库

⭐️ 静态链接

  静态链接就是把我们的.o文件和使用到库中的文件链接形成可执行程序的过程,在我们使用某个库的时候,可能我们所使用到的这个库可能也依赖其他库,那么对他也需要链接,但是这个琐碎的步骤是由链接器完成的。
  对于静态链接你可以把它理解成拷贝其他文件代码的过程,需要哪里就拷贝哪里,最后形成的可执行程序不再依赖其他库就能运行。

⭐️ 动态链接

  动态链接的基本思想是把程序按照模块拆分成各个相对独立的部分,在运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行程序。
  动态链接形成的可执行程序每次运行都需要链接,重定位是不是很慢,确实,它相较于静态链接形成的可执行程序会慢,但是它有自己的解决方法即是延迟绑定,延迟绑定的基本思想就是:当函数第一次使用到的时候才会进行绑定,对于未使用到函数不会进行绑定。

⭐️ 对比

  动态链接节省资源但依赖库
  静态链接浪费资源但不再依赖库
我们对一个相同的程序分别采用两种链接方式生成一个可执行程序看一看。
在这里插入图片描述  file1和file2分别是动态链接和静态链接形成的可执行程序,我们通过上表可以明显看出他们在体积上的差别。
  对于静态库的安装

sudo yum install -y glibc-static
sudo yum install -y libstdc+±static

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

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

相关文章

G1收集器简介

G1是一款既收集新生代又收集老年代的收集器,使用它可以实现整个java堆的gc,它有两个非常重要的新概念:region和remember set(简称rset)。 region是啥? G1中虽然保留了新生代和老年代的概念,但是…

Spring Boot程序输出远程访问IP

🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的博客专栏《SpringBoot开发》。🎯🎯…

03 HAL库下UART的使用

引言: 需要使用到的uart调试工具在文章最后的资料里面 题外话:uart和usart的区别 UART(Universal Asynchronous Receiver/Transmitter)和USART(Universal Synchronous/Asynchronous Receiver/Transmitter)…

获取Windows10系统原始安装日期

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl 目标 获取Windows10系统最原始的安装日期;例如:刚买电脑时安装系统的时间。 步骤 第一步,请打开PowerShell,单击Windows P…

评价类问题:层次分析法

引言:打分法 一、解决评价类问题的三个方面 二、如何寻找评价准则 三、如何确定权重 (1)分而治之的思想 (2) 层次分析法思想: 指标重要性(满意程度) 判断矩阵(正反矩…

面试题-JVM 初级面试题(40道含答案)

author: 小郑说编程 JVM 初级面试题 1、对象在哪块内存分配? 数组和对象在堆内存分配;某些对象没有逃逸出方法,可能被优化为在栈上分配 2、谈谈 JVM 中的常量池 JDK 1.8 开始 字符串常量池:存放在堆中,包括 Strin…

分布式【4. 什么是 CAP?】

什么是 CAP? C 代表 Consistency,一致性,是指所有节点在同一时刻的数据是相同的,即更新操作执行结束并响应用户完成后,所有节点存储的数据会保持相同。 A 代表 Availability,可用性,是指系统提…

[原创][R语言]股票分析实战[6]:正则表达式提取子字符串

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XX QQ联系: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、D…

LeetCode75| 滑动窗口

目录 643 子数组最大平均数 | 1456 定长子串中元音的最大数目 1004 最大连续1的个数 ||| 1493 删掉一个元素以后全为1的最长子数组 643 子数组最大平均数 | class Solution { public:double findMaxAverage(vector<int>& nums, int k) {double sum 0;double re…

数据结构与算法笔记

数据结构&#xff1a; 就是指一组数据的存储结构 算法&#xff1a; 就是操作数据的一组方法 数据结构和算法 两者关系 数据结构和算法是相辅相成的。数据结构是为算法服务的&#xff0c;算法要作用在特定的数据结构之上。 数据结构是静态的&#xff0c;它只是组织数据的一…

Quartz调度引擎基于MySQL的高可用架构调度延迟分析与解决方案

1、Quartz默认使用的高可用架构 在Quartz的官方文档中&#xff0c;介绍了一种默认的高可用架构&#xff0c;基于数据库实现。该方案中&#xff0c;多台Quartz服务器连接同一个数据库&#xff0c;单台服务器每次调度检索并锁定一批Trigger用于触发&#xff0c;锁定过程中将先从…

Kubernetes网络-VXLAN

一. 网络基础 1. 计算机网络的分层 如今连接方式也越来也丰富&#xff0c;网线、WiFi、蓝牙、光纤&#xff0c;甚至我们普通的电线、照明所用的灯光&#xff0c;都可以作为接入网络的介质。如此庞大的网络&#xff0c;丰富多样的设备&#xff0c;计算机网络技术能把它们统一起…

认识计算机网络——计算机网络的组成

计算机网络是由多个计算机和网络设备组成的系统&#xff0c;通过通信协议实现数据传输和信息交换。它是现代社会信息技术的重要支撑&#xff0c;广泛应用于各个领域。本文将介绍计算机网络的主要组成部分&#xff0c;包括硬件设备、软件协议和网络服务。 一、硬件设备 计算机网…

46、激活函数 - Relu 激活

本节介绍一个在神经网络中非常常见的激活函数 - Relu 激活函数。 什么是ReLU激活函数 ReLU 英文名为 Rectified Linear Unit,又称修正线性单元,是一种简单但很有效的激活函数,它的定义如下: 即当输入 x 大于零时,输出等于他自己;当输入小于等于零时,输出为零,下面是re…

【Android进阶篇】Android中PreferenceScreen的作用和详细用法介绍

1&#xff0c;PreferenceScreen的作用 在Android开发中&#xff0c;PreferenceScreen是一个非常重要的布局控件&#xff0c;主要用于创建设置界面&#xff08;settings page&#xff09;。它可以包含多个Preference子项&#xff0c;如CheckBoxPreference, ListPreference等&am…

C++继承与派生——(8)多继承

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 苦难和幸福一样&#xff0c;都是生命盛…

【多传感器融合导航论文阅读】

多传感器融合导航论文积累 知识点总结因子图一致因子图 文献阅读笔记[IF 18.6] 知识点总结 因子图 Factor Graph 是概率图的一种&#xff0c;是对函数因子分解的表示图&#xff0c;一般内含两种节点&#xff0c;变量节点和函数节点。 因子图存在着&#xff1a;两类节点&#…

主浏览器优化之路1——你现在在用的是什么浏览器?Edge?谷歌?火狐?360!?

上一世&#xff0c;我的浏览器之路 引言为什么要用两个浏览器为什么一定要放弃火狐结尾给大家一个猜数字小游戏&#xff08;测运气&#xff09; 引言 小时候&#xff0c;我一开始上网的浏览器是2345王牌浏览器吧&#xff0c; 因为上面集成了很多网站&#xff0c;我记得上面有7…

使用axios发送get和post请求

使用axios发送get和post请求的方法如下&#xff1a; 1.发送GET请求&#xff1a; axios.get(url).then(response > {// 请求成功的处理逻辑console.log(response.data);}).catch(error > {// 请求失败的处理逻辑console.error(error);});2.发送POST请求&#xff1a; ax…

Loading 加载 Taro + vue3 自定义组件的封装和 分页 优化

1.需求 当需要实现一个组件 上拉加载的组件 我们可以选择某些组件库的组件。 但是有的组件没有这个组件&#xff0c;比如跟Taro 框架配套的京东nut-ui组件库 没有提供这个功能, 2.Loading组件 ①封装 <template><div class"container"><div class&…