c++函数为什么带imp_算法

2023-11-15

算法数学之美 45de99cc960a735db54d4326bbc70af1.png

日期:2018年1月13日

正文共:2779字3图

预计阅读时间:7分钟    

来源:周平

bfde2d4477803f4e8ecfaf4847b4813b.png

最近网易云课堂开放了一节叫Linux内核分析的课程。一直对操作系统和计算机本质很感兴趣,于是进去看了下,才第一堂课,老师就要求学生写一篇关于课时1的博客作为作业。对于这种新颖的作业形式,笔者相当惊讶。好吧,作为任务,还是完成一下吧,刚好需要消化一下。本文将会按照要求,将一段C语言代码编译成汇编,并给予分析和自己的思考。

首先对会涉及到的一些CPU寄存器和汇编的基础知识罗列一下:

●16位、32位、64位的CPU寄存器名称有所不同,比如指令地址寄存器ip,在16位中叫ip,32位中叫eip,64位叫rip

●32位的汇编指令通常以l结尾,比如movl相当于mov的含义

●ebp : 堆栈基地址 寄存器,这个寄存器保存的是当前执行绪的栈底地址

●esp : 堆栈栈顶 寄存器,这个寄存器保存的是当前执行绪的栈顶地址

●eip : 指令地址 寄存器,这个寄存器保存的是指令所在的地址,CPU会不断的根据eip所指向的指令去内存取指令并执行,并自行累加取下一条指令逐条执行。eip无法直接赋值,call、ret、jmp等指令可以起到修改eip的作用

●%用于直接寻址寄存器,$用于表示立即数。movl $8, %eax表示把立即数8存到eax中

●()用于内存间接寻址,比如movl $10, (%esp)表示将立即数10保存到esp所指向的内存地址中

●8(%ebp)表示先找到 ebp所指向的地址值+8后得到的地址

●栈地址值是向下增长的,即栈顶从高地址向低地址移动

准备工作

准备一段C代码:

int g(int x)

{

return x+5;

}

int f(int x)

{

return g(x);

}

int main(void)

{

return f(10)+1;

}

使用实验楼环境

7f2c9534741eb48a790dc0102a7f131b.png

编译成汇编代码

使用如下命令编译上面的c代码

gcc -S -o main.s main.c -m32

去掉不重要的部分后,得到:

汇编代码结果为:

g:

        pushl%ebp

        movl%esp, %ebp

        movl8(%ebp), %eax

        addl$5, %eax

        popl%ebp

        ret

f:

       pushl%ebp

       movl%esp, %ebp

       subl$4, %esp

       movl8(%ebp), %eax

       movl%eax, (%esp)

       callg

       leave

       ret

main:

       pushl%ebp

       movl%esp, %ebp

       subl$4, %esp

       movl$10, (%esp)

       callf

       addl$1, %eax

       leave

       ret

分析

具体的逐步分析,这里就省了,老师课上讲的很详细了,这里主要是要进行思考和归纳。

首先,我们看到3个C函数对应生成了3个部分的汇编代码,分别用函数名作为标号隔开了

int g(int x) -> g:

int f(int x) -> f:

int main(void) -> main:

我们知道程序是从main函数开始执行的,那么当程序被加载并运行时,上面的汇编代码会被加载到内存的某一个区域。而且,CPU中的很多寄存器都会初始化,当然其中最重要的是eip,因为eip是指向下一条将要执行的命令所在的内存地址,所以此时的eip应该指向main标号下的pushl %ebp:

main:

eip ->  pushl %ebp

程序开始执行…

我们捆绑着看,首先先看这两条:

pushl%ebp

movl%esp, %ebp

再观察一下整个代码,有没有发现不仅仅是main函数,函数f和g的开头也是这两个指令。分析一下,不难得出,这两条指令是指将当前栈基地址压栈后,重新将基地址定位到栈顶,这个含义其实是保存好当前的基地址,重新开始一个新的栈。由于函数可以调函数,这里的当前基地址,实际上是上一个函数的栈基地址。例如,在f函数中的这两句指令,实际上保存的是main函数的栈基地址。

接着来分析两句:

subl$4, %esp

movl$10, (%esp)

对照C代码不难发现,这是参数进栈,将立即数10,保存到栈顶(esp所指向的内存地址是栈顶)。而在f函数中也可以发现类似的语句:

subl$4, %esp

movl8(%ebp), %eax

movl%eax, (%esp)

所以,我们可以得出结论是,在调用函数前需要把参数逐个压栈,而压栈的顺序根据笔者的测试是从右向左的。

接着调用call指令,跳转到f函数,我们知道call指令等同于下面的伪代码:

pushl %eip+1

movl %eip f

即把call指令的后一条指令进栈后,将eip赋值为目标函数的第一个指令地址。这样做显而易见:当所调用的函数结束后,需要返回当前函数继续执行,所以必须要保存下一条指令,否则回来的时候就找不到了。

来到f函数,首先是保存main函数的栈基地址,然后需要调用g函数,于是需要参数先进栈:

subl$4, %esp

movl8(%ebp), %eax

movl%eax, (%esp)

这里重点思考一下,f函数是如何获得main函数传递过来的参数的,我们看到

movl8(%ebp), %eax

为什么参数是从8(%ebp)中获得的呢?我们知道8(%ebp)表示的是以ebp为基准向栈底回溯8个字节得到,为什么是8个字节呢?

回想一下,在main函数中完成了参数进栈后做了两件事情:

1.由于call f指令的作用,call f下一条指令的地址被压栈了,这占用率4个字节

2.进入f函数后,立即将main函数的栈基地址进栈了,而且将ebp靠向了栈顶esp,这又占用了4个字节

于是通过8(%ebp)可以找到前一个函数的第一个整型参数的值。

一张图告诉你怎么回事:

看过了进入函数,调用函数的过程,再看一下函数是如何退出的。观察main和f不难发现,退出函数使用的是如下指令

leave

ret

leave指令相当于如下指令:

movl%ebp, %esp

popl%ebp

●第一条语句是将esp重置到ebp,可以理解为清空当前函数所使用的栈

●第二条语句是将栈顶值赋值给ebp,并弹出,栈顶值是什么呢?通过上面的分析不难发现,此时的栈顶值实际上是前一个函数的栈基地址,所以第二条语句的意思就是把ebp恢复到前一个函数的栈基地址

接着ret就是相当于,恢复指令指向:

popl %eip
为什么g函数没有leave呢?
因为g函数内部没有任何的变量
声明和函数调用栈一直都是空的,
所以编
译器优化了指令

总结

最后,通过这个例子,总结一下函数调用的过程:

进入函数:

当前栈基地址压栈(当前栈基地址实际上是前一个函数的栈基地址)

调用其他函数:

1.参数从右到左进栈

2.下一条指令地址进栈

退出函数:

1.栈顶esp归位,回到本函数的ebp

2.基地址回退到上一个函数的基地址

3.eip退回到上一个函数即将要执行的那条语句的地址上


来自:P_Chou Tech Space

- End - -

更多精彩:

☞ 算法|我们为什么要思考算法

☞ 丘成桐在CNCC会议的演讲全文

☞ 为什么Java开发人员都带眼镜 | 程序员搞笑段子合集

☞ 程序观点下的线性代数

☞ 常见的几种算法思想,你都会么?

☞ 深度揭秘!中国程序员生存现状

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++函数为什么带imp_算法 的相关文章

  • dbfread报错ValueError错误解决方法

    问题 我在用dbfread处理 dbf数据的时候出现了报错 ValueError could not convert string to float b 然后查找 dbf源文件的时候 发现在报错的那一行数据中 有一列甚至好几列的数据中出现了
  • 牛客题目——最长无重复子数组、分糖果问题、旋转数组

    文章目录 题目1 最长无重复子数组 解题思路 代码实现 题目2 分糖果问题 解题思路 代码实现 题目3 旋转数组 解题思路 代码实现 题目1 最长无重复子数组 给定一个长度为n的数组arr 返回arr的最长无重复元素子数组的长度 无重复指的
  • volatility内存取证分析与讲解(持续更新)

    volatility内存取证分析与讲解 0x01 volatility的安装 0x02 基本使用 0x03 取证实战 持续更新 0x04 总结 0x01 volatility的安装 本人暂时只使用windows下的volatility进行取
  • AttributeError: Model object has no attribute predict_classes 的解决方案

    第一次用的网络是在model Sequential 下添加模块的的方法 也就是所谓的顺序模型 Sequential class可以使用model predict classes 的方法来实现预测 代码如下 model Sequential
  • android 七巧板布局,iOS界面视图布局框架 – TangramKit

    TangramKit logo TangramKit是一套在Swift3 0语言上开发的iOS界面视图布局框架 它的名字来源于中国古代的玩具七巧板 寓意着可以用简单的功能来构造出各种千变万化且非常复杂的UI界面 TangramKit的内核是
  • 静态分析之数据流分析与 SSA 入门 (二)

    什么是静态单赋值 SSA SSA 是 static single assignment 的缩写 也就是静态单赋值形式 顾名思义 就是每个变量只有唯一的赋值 以下图为例 左图是原始代码 里面有分支 y 变量在不同路径中有不同赋值 最后打印 y
  • 【pytorch】使用model.eval()和torch.no_grad()以及requires_grad = False之间的区别

    model eval 是将模型切换到评估模式 这意味着在模型中使用的一些操作 例如Dropout和BatchNorm 将不会在评估模式下运行 而是使用预定义的值 这对于在测试集上进行推理时很有用 with torch no grad 是一个
  • 【LaTeX入门】11 文本居中

    首先给大家分享一个巨牛巨牛的人工智能教程 是我无意中发现的 教程不仅零基础 通俗易懂 而且非常风趣幽默 还时不时有内涵段子 像看小说一样 哈哈 我正在学习中 觉得太牛了 所以分享给大家 点这里可以跳转到教程 centerline 语法 ce
  • App违法违规收集使用个人信息自评估指南(史宾格隐私合规检查项)

    隐私政策文本 隐私政策的独立性 易读性 是否有隐私政策 在APP界面中能够找到隐私政策 包括通过弹窗 文本链接 常见问题等形式 隐私政策是否单独成文 隐私政策以单独成文的形式发布 而不是作为用户协议 用户说明等文件中的一部分存在 隐私政策是
  • 【Java】无数据源启动

    前言 在开发项目的过程中 经常碰到框架使用SpringBoot进行开发 但是却不需要连接数据库的方式 本篇文章详细记录了具体的解决方法与方式希望能对您有所帮助 一 启动类配置 代码如下 import org springframework
  • 有哪些老程序员都知道对新程序员很有用的经验

    回想起自己刚步入职场的时候 接到任务的心态就是尽快搞完 只要没做完就怕耽误了整个团队 还怕领导觉得自己能不行 怕被开除等等 但是每次完成之后 都有错误 编译通过了 逻辑又有问题 自己就是不断的修改当中 时间久了自己写的代码自己都不愿意看 因
  • CMMI 级别中和BUG率

    关于CMMI 级别中和BUG率相关的信息如下 千行代码缺陷率 bug率 CMM1级 11 95 CMM2级 5 52 CMM3级 2 39 CMM4级 0 92 CMM5级 0 32 基本属于成倍递减 国内通过CMMI 5 级评定的IT行业
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • C/C++编程题刷题:leetcode 62. 不同路径 和 63. 不同路径 II

    62 不同路径 一个机器人位于一个 m x n 网格的左上角 起始点在下图中标记为 Start 机器人每次只能向下或者向右移动一步 机器人试图达到网格的右下角 在下图中标记为 Finish 问总共有多少条不同的路径 例如 上图是一个7 x
  • Filter(过滤器)常见应用

    javaweb学习总结 四十六 Filter 过滤器 常见应用 一 统一全站字符编码 通过配置参数charset指明使用何种字符编码 以处理Html Form请求参数的中文问题 1 package me gacl web filter 2
  • 【深度学习】Faster R-CNN+win10+tensorflow1.12.0+python3.6+CUDA9.0+cudnn7.3配置

    Faster R CNN win10 tensorflow1 12 0 python3 6 CUDA9 0 cudnn7 3配置 一 源码下载 二 修改 lib 下部分文件 编译Cython模块 三 安装COCO API 四 下载数据 五
  • 【Leetcode】225. 用队列实现栈

    题目描述 请你仅使用两个队列实现一个后入先出 LIFO 的栈 并支持普通栈的全部四种操作 push top pop 和 empty 实现 MyStack 类 void push int x 将元素 x 压入栈顶 int pop 移除并返回栈
  • Selenium+Firefox的自动下载(去掉下载弹窗)

    Selenium Firefox的自动下载 去掉下载弹窗 一 去掉下载弹窗的优点 二 去掉下载弹窗的一般命令 三 重点 一 去掉下载弹窗的优点 检索键盘鼠标自动化控制模块的导入 可以无头化运行 不影响同时进行的其他的任务 二 去掉下载弹窗的

随机推荐

  • Apache Avro 文档概况

    文章主题来源于英文源文档 gt Apache Avro 1 11 0 文档 Apache Avro 1 11 0 Documentation 1 介绍 Apache Avro 是一个数据序列化 data serialization 框架 A
  • CentOS 7 下安装Chrome浏览器

    参考 http www cnblogs com hfyfpga p 6261819 html http blog csdn net johnnyhu90 article details 42127521 如果出现如下错误 curl 7 Fa
  • rust类型转换

    类型转换 类型转换分为隐式类型转换和显式类型转换 隐式类型转换是由编译器完成的 开发者并未参与 所有又称强制类型转换 显式类型转换是由开发者指定的 就是一般意义上的类型转换 一 显式转换 一 as 1 原生类型之间的转换 Rust不提供原生
  • OpenSSL SSL_read: Connection was reset, errno 10054

    一 问题描述 提交代码到github上 git报错如下 OpenSSL SSL read Connection was reset errno 10054 看报错提示 极大的可能是由于网络不稳定 连接超时导致的 如果多次尝试重新提交后 仍然
  • 解决:Cannot resolve plugin org.apache.maven.plugins:maven-compiler-plugin:3.1问题

    解决 Cannot resolve plugin org apache maven plugins maven compiler plugin 3 1问题 问题 Cannot resolve plugin org apache maven
  • 拳王虚拟项目公社:0基础小白副业年赚30W玩法大揭秘!看懂一半至少年赚10W!

    今天 我们分享一个励志的案例 一位工程师兼职创业 每天不超过3个小时 副业年收入30W 这个案例对我的触动和启发也非常大 我们主要分享 1 一类规模庞大的群体和一个小众冷门领域 他切下的蛋糕有多大 2 战术上的勤奋永远赚不了大钱 怎样的战略
  • C语言生成20个随机二位整数求奇偶个数并且从小到大输出

    这道题考察的是生成随机二位整数保存是要保存在一个容量为20的数组中 然后再循环进行判断奇偶性进行求个数和求和最终输出奇数平均值和偶数的和 最后采用冒泡排序对这个数组进行排序 冒泡排序可以看我之前的文章最终输出就行了 代码如下 include
  • 用MSYS2安装mingw

    文章目录 前言 卸载mingw 安装MSYS2 前言 安装MSYS2的原因是 在windows安装protobuf时 想用mingw编译protobuf的库 而protobuf的官方手册只给出一句 To build from source
  • IDEA常用快捷键

    Intellij IDEA常用快捷键 Ctrl E 显示最近修改的文件列表 Ctrl Shift Backspace Ctrl alt 左右方向键 跳转到上次编辑的地方 Ctrl F12 可以显示当前文件的结构 Ctrl Shift Ins
  • 【ICKIM 2022】第四届知识与信息管理国际会议

    2022 知识与信息管理国际会议 ICKIM 2022 第四届国际知识与信息管理会议 作为WSSE的研讨会 将于2022年9月28日至30日在厦门举行 一 会议出版 会议被接收的文章将出版到ACM 会议论文集 ISBN 978 1 4503
  • mybatis中的options注解

    mybatis的 Options注解能够设置缓存时间 能够为对象生成自增的key 场景 一个表id 主键 设置为自增 而当我们需要在dao层插入数据的时候立刻获取到该自动生成的id 实现 如下 Insert insert into inst
  • 内存检测工具Dr.Memory在Windows上的使用

    之前在https blog csdn net fengbingchun article details 51626705 中介绍过Dr Memory 那时在Windows上还不支持x64 最新的版本对x64已有了支持 这里再总结下 Dr M
  • 抽象工厂方法

    在工厂方法中 我们可以很方便的创建一个产品继承结构下的多个产品 那么考虑这么一种情况 我们需要在工厂中创建多个不同继承结构的产品 例如皮肤库 包含按钮 文本框 选择框等多个不同的元素 Spring皮肤库包含了一组相似的按钮 文本框 选择框
  • Window10手把手带你YOLOV5的火焰烟雾检测+tensorrt量化加速+C++动态库打包

    目录 0 引言 1 yolov5模型训练 1 2 模型训练 1 3 模型测试 2 模型转换 2 1 pt wts engine 2 1 1 pt转wts 2 1 2 wts转engine 3 动态库打包 0 引言 本人配置 win10 py
  • 简单实现点击el-tab-pane中的子组件按钮切换el-tabs

    简单实现点击el tab pane中的子组件按钮切换el tabs 实现效果 点击单条查看详细信息 实现跳转到详细信息面板 实现步骤 1 在父组件中声明函数 二 在要实现点击跳转的子组件中设置点击事件 主要的原理就是 tabs组件的面板激活
  • python笔记5-循环for和while

    1 for 获取列表中的项 for name in Christopher Susan print name name为变量 in后面的是循环列表 for会自动遍历列表 输出结果 Christopher Susan 指定循环次数 for i
  • WEB架构师成长之路之3:要懂哪些知识

    Web架构师究竟都要学些什么 具备哪些能力呢 先网上查查架构师的大概的定义 参见架构师修炼之道这篇文章 写的还不错 再查查公司招聘Web架构师的要求 总结起来大概有下面几点技能要求 一 架构师有优秀的编码能力 解决开发人员无法解决的难题 二
  • 伽罗华有限域的FEC

    FEC算法 cloudfly cn的博客 CSDN博客 fec算法 I 基于IP的语音和视频通话业务为了实时性 一般都是采用UDP进行传输 基站无线一般配置UM模式的RLC承载 因此丢包是不可避免的 在小区信号的边沿则丢包率会更高 为了通话
  • CloudCompare学习笔记(一)-- 界面初识

    本人也是cc纯小白 博客只用来记录学习内容和一些不懂的地方 如有错误还望指正 一 主界面 CC的界面大致可以分为以下几块 中间部分 1 DB Tree 打开的文件或者创建的实体都会存放在这里 2 Properties 选择的文件的属性 在打
  • c++函数为什么带imp_算法

    算法数学之美 日期 2018年1月13日 正文共 2779字3图 预计阅读时间 7分钟 来源 周平 最近网易云课堂开放了一节叫Linux内核分析的课程 一直对操作系统和计算机本质很感兴趣 于是进去看了下 才第一堂课 老师就要求学生写一篇关于