你真的懂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.hqwc.cn/news/311473.html

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

相关文章

Good Bye 2023

Good Bye 2023 Good Bye 2023 A. 2023 题意:序列a中所有数的乘积应为2023,现在给出序列中的n个数,找到剩下的k个数并输出,报告不可能。 思路:把所有已知的数字乘起来,判断是否整除2023,不够…

网络Ping不通故障定位思路

故障分析 Ping不通是指Ping报文在网络中传输,由于各种原因(如链路故障、ARP学习失败等)而接收不到所有Ping应答报文的现象。 如图1-1所示,以一个Ping不通的尝试示例,介绍Ping不通故障的定位思路。 图1-1 Ping不通故…

9. 进程

9. 进程 1. 进程与程序1.1 main() 函数由谁调用1.2 程序如何结束1.2.1 注册进程终止处理函数 atexit() 1.3 何为进程1.4 进程号 2. 进程的环境变量2.1 应用程序中获取环境变量2.1.1 获取指定环境变量 2.2 添加/删除/修改环境变量2.2.1 putenv()2.2.2 setenv()2.2.3 命令行式添加…

【Spring Security】认证之案例的使用、MD5加密、CSRF防御

目录 一、引言 1、什么是SpringSecurity认证 2、为什么使用SpringSecurity之认证 3、实现步骤 二、快速实现(案例) 1、添加依赖 2、配置 3、导入数据表及相关代码 4、创建登录页及首页 5、创建配置Controller 6、用户认证 6.1、用户对象User…

Linux磁盘与文件管理

目录 一、磁盘介绍 1. 磁盘数据结构 2. 磁盘的接口类型 3. 磁盘在Linux上的表现形式 二、磁盘分区与MBR 1. 分区优缺点 2. 分区方式 3. MBR分区 4. GPT分区 三、文件系统 1. 文件系统的组成 2. 默认的文件系统 3. 文件系统的作用 4. 模拟破坏文件与修复文件 4…

喜迎元旦 | 愿新年,胜旧年,百华鞋业祝您元旦快乐,万事胜意!

一年复始岁序开,万象更新启新华 2023年我们聚力同行,相融共生, 凝心携手,奋进前行。 2024年我们挟着未知,带着期待, 继续携手砥砺前行 踏上新征程,向着新的奋斗目标再出发。 元旦&#xff…

四种常见智能指针的介绍

一、介绍 当类中有指针成员时,一般有两种方式来管理指针成员:一是采用值型的方式管理,每个类对象都保留一份指针指向的对象的拷贝;另一种更优雅的方式是使用智能指针(smart pointer),从而实现指针指向的对象的共享。 …

蓝桥杯C/C++程序设计——特别数的和

题目描述 小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。 请问,在 1 到 n 中,所有这样的数的…

GitHub 一周热点汇总 第3期 (2023/12/24-12/30)

GitHub一周热点汇总第三期 (2023/12/24-12/30),梳理每周热门的GitHub项目,了解热点技术趋势,掌握前沿科技方向,发掘更多商机。元旦就要到了,提前祝大家新年快乐。 #1 StreamDiffusion 项目名称:StreamDiff…

vue项目表单使用正则过滤ip、手机号

import useFormValidate from /hooks/useFormValidatesetup(props, { emit }) {const { validateName, validateIPAndPort } useFormValidate()const state reactive({workFaceInfo: props.info?.id ? props.info : {},sysTypeData: props.sysType,formRules: {name: [{req…

wsl中的Ubuntu安装远程桌面

wsl Ubuntu默认只能打开命令行,看不到图形化界面,有些操作不方便。这里介绍两种方法来远程连接到wsl里 VNC 因为win10的wsl不支持systemd, 所以这种方式只能是Windows11的系统,Window10只能用xrdp 1、禁用WSLg 在c:\users\用户…

12.31_黑马数据结构与算法笔记Java

目录 345 设计跳表 Leetcode 1206 346 设计最小栈 Leetcode 155 347 设计端网址 Leetcode 355 348 设计推特 Leetcode 355 349 股票系列问题 Leetcode 121 350 股票系列问题 Leetcode 122 351 股票系列问题 Leetcode 714 352 股票系列问题 Leetcode 309 353 股票系列问…