函数栈帧的创建与销毁(保姆级讲解)

news2024/11/24 11:09:20

局部变量是怎么创建的?

在为main函数开辟栈帧空间时,在一定范围内初始化成0CCCCC,再把里面0CCCC的一些开辟空间给局部变量使用。

为什么局部变量的值是随机值?

因为我们在为main函数开辟栈帧空间时,会将一定范围内空间初始成0CCCCCC里面什么也没有,所以如果局部变量不给初始化,局部就会进入随意开辟栈帧空间,就是为随机值或者是烫烫烫。

函数是怎么传参的?传参的顺序是怎样的?

eax(b)和ecx(a)进行压栈,先传的b再传的a,从右向左。

形参和实参是什么关系?

形参是实参的一份临时拷贝,它们的值是相同的,但所使用的空间是不同的,所以形参的改变不影响实参,形参确实只是实参的一份临时拷贝。

函数调用是怎么做的?

下面我画图所解释的非常清楚了。(如果不明白的同学可以私信互相交流下)

函数调用是结束后怎么返回的?

由函数一步一步建立空间,再一步一步销毁空间返回

用保存call指令下一条指令地址与ebp-main函数的保存位置进行寄存器返回值

用寄存器eax返回最终的值。


知道和函数栈帧的创建和销毁就都会了,其实就是修炼了自己的内功,也能搞懂后期更多的知识。
进入正题
今天讲解使用的环境是VS2019
同时在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,具体细节取决于编译器的实现。

首先我们要了解什么是函数栈帧

函数栈帧就是在函数调用过程中,程序为函数所开辟的栈空间,函数一般放在栈区。

而编译器为了方便动态内存管理,一般划分为了三个区域:栈区 堆区 静态区

而什么又是栈呢?

栈的概念及结构
栈:一种特殊的线性表,其只允许在 固定的一端 进行 插入和删除 元素操作。 进行数据插入和删除 操作的一端称为 栈顶 ,另一端称为 栈底 。栈中的数据元素遵守 后进先出 LIFO (Last in First Out) 的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈(Push), 入数据在栈顶
出栈: 栈的删除操作叫做出栈(Pop)。 出数据也在栈顶 。
特点:栈只能在栈顶进行插入和删除。

为了更加清楚了解函数栈帧,我们还需要了解下以下寄存器

eax :保留临时数据,常用于返回值

ebx:保留临时数据

ecx

edx

ebp:栈低指针

esp:栈顶指针

ebp和esp这两个寄存器存放的是地址,这两个地址用来维护函数栈帧的。

每一个函数调用,都要在栈区创建一个空间。

接下来以如下图代码来解释函数栈帧的创建与销毁过程

#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;

	c=Add(a, b);

	printf("%d\n", c);

	return 0;
}

在调用main函数时ebp和esp会维护main函数的函数栈帧。

当我按F10进入调试,并且打开调用堆栈


我们可以看见函数的调用关系,调用堆栈是反应函数的调用关系的

由调试我们可以看见main函数是被invoke_main()函数调用的

而Add函数是被main()函数调用的

那么理所当然的invoke_main函数与Add函数也是有自己的函数栈帧空间的,并且由ebp和esp来维护函数栈帧空间。

F10进入调试点击右键转到反汇编

如下图所示是此次代码的反汇编指令 

但我们为了方便更加清晰的观察,再次点右键取消显示符号名。

 

main函数第一条反汇编指令是Push(压栈) ebp。

如果是ebp压栈那么ebp的值是会变小的 因为是由高地址到低地址

这是原来的值

当我push以后的值,果然变小了

 所以是真的压进去了吗?我们可以通过内存来查看

当我们查看esp内存的地址时,ebp确实压进去了,因为esp内存的值是ebp的地址

fc f9 f3 00  VS编译器一般是:小端字节存储 低位字节数据放低地址处 高位字节数据放高地址处 

mov的意思是把esp的值给ebp 我们依然可以通常调试来查看是不是这样的

sub是subtraction减法的缩写。就是给esp减去0E4h 

 用16进制显示就是228

 当我们给esp减去所对应的值时,那么esp不能再指向原来的位置,而是指向了上一块的某块区域

当我们查看内存ebp和esp

ebp

esp

这些内存空间都是为main函数所开辟的空间 

接下来是在栈顶压3个元素 

随着压栈esp也会随着压栈指向位置会发生变化 

 

VS2019栈区内存存放习惯:先放高地址,再放低地址

下面esp的值会随着压栈 esp位置也产生了变化

lea指令是=load effective address. 意思是加载有效地址

 这个指令有效果的其实是rep stos意思是把edi开始下面将内存空间改成OCCCCCCCCh 以双倍字节开辟 dword=double word  es:[edi]把edi开始下面所有空间以双字节开辟成0CCCCCCCCh。

edi到ebp开辟内存空间

esp-24h的值如下图

 

 将十进制数0Ah放进ebp-8 就是相当于把10放到了ebp-8里面

如果不给a初始值那么就是随机值,因此在为main函数开辟空间时使用的就是CCCCCC的值,所以会出现烫烫烫(字符串 字符)或者随机值(变量)。

 

0a 00 00 00  就是10的十六进制存储 内存存储一般是十六进制存储

又是隔着两个字节存放的C的值0 (不同编译器存放位置不同,取决于编译器)

 

把20的值给eax再把eax压栈压进去

下一步指令把10给ecx然后再把ecx进行压栈

 按F11进入call令

内存中存放的是下一条add指令的地址00171987

 当我们再按一次F11会跳到Add函数的反汇编指令当中去

这和main函数的反汇编极其相似,这是在为Add函数开辟函数栈帧空间

 第一步Push压栈 第二步mov esp给ebp 第三步把0cch 给esp 相当于esp又往上走了

 

 

 

再进行压栈 

随着压栈esp的值也变化 esp的指向位置也随着变化

把ebp-0ch值给edi 把3给ecx 然后从edi开始下面所有位置改成0CCCCCCCH

 

 

 把0放到ebp-8 

 

 

 

 

ebp+0ch相当于+12 epb+12

通过调试过程看见传参是从右向左,先传的b再传的a

最后返回的时候把ebp-8的值也就是z的值给了寄存器eax

下面指令pop三次 esp也随着产生位置变化

当pop了三次esp的值 增加了3次

当pop三次要返回main函数 那么Add创建的函数栈帧就要销毁 这几个指令完成了把esp的值给ebp

再pop ebp 我们这个位置所保存main函数的ebp-main 就是为了函数返回时找到main函数的ebp

返回以后ebp和esp又开始维护了main函数的函数栈帧空间

这条指令就是为了执行call指令下一条add的功能 弹出了add指针地址

所以我们在开辟函数栈帧时保存了add指令地址

 当我们再按F10就回到了main函数Add的位置

main函数add指令 00171987与我们刚才在函数栈帧保存的call指令下一条指令的地址一模一样,也就是add指令函数的地址。就是为了方便回来,简直就是荣归故里!设计的太牛了!

给esp+8

随着esp+8形参的空间也销毁了。

把eax 30的值给ebp-20h 就是刚才c的位置

然后在程序结束时寄存器会返回所对应的值。

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

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

相关文章

【宏实现二进制奇偶位交换】

文章目录 一. 二进制奇偶位交换说明意思&#xff1f;二. 解题思路三. 代码验证四. 总结 一. 二进制奇偶位交换说明意思&#xff1f; 就是一个int类型的整数在操作系统下是32位二进制01序列&#xff0c;第一位和第二位交换&#xff0c;第二位和第三位交换&#xff0c;依次类推。…

口袋参谋:生意参谋指数转换工具,比对手更了解对手!

​所谓“知己知彼&#xff0c;百战不殆”&#xff0c;比对手更了解对手&#xff0c;就是提升自己的好机会。 在竞争如此激烈的淘宝天猫上&#xff0c;淘宝平台为了保护商家店铺数据&#xff0c;将真实数值全部隐藏&#xff0c;变成了指数。 所以我们查看市场排行、市场大盘、竟…

awvs安装教程和使用

awvs安装和使用 文章目录 awvs安装和使用安装&#xff08;awvs&#xff09;Acunetix-v23.9-Windows1 安装前准备1.1 查看帮助文档&#xff0c;修改此文件1.2 追加hosts 参数1.3 根据REANME.txt帮助文档提示&#xff0c;启动工具(Now install the tool)&#xff0c;双击 1.4 安装…

(vue3)大事记管理系统 文章管理页

[element-plus进阶] 文章列表渲染&#xff08;带搜索&到分页&#xff09; 表单架设&#xff1a;当前el-form标签配置一个inline属性&#xff0c;里面的元素就会在一行显示了 中英国际化处理&#xff1a;App.vue中el-config-provider标签包裹组件&#xff0c;意味着整个组…

Cesium 问题:加载 geojson 文件后使用 remove 方法移除,但浏览器内存会持续增长并为得到释放直到浏览器崩掉

文章目录 需求分析解决需求 在开发中,加载了 geojson 文件后,浏览器内存会增长,当使用remove方法移除后再次添加,浏览器内存持续增长并未减少,直到第三次交互添加,浏览器崩掉了 分析 在处理大量实体或长时间运行的应用程序中,及时释放不需要的实体是一种良好的做…

数据结构 - 3(链表12000字详解)

一&#xff1a;LinkedList的使用 1.1 ArrayList的缺陷 上篇文章我们已经基本熟悉了ArrayList的使用&#xff0c;并且进行了简单模拟实现。由于其底层是一段连续空间&#xff0c;当在ArrayList任意位置插入或者删除元素时&#xff0c;就需要将后序元素整体往前或者往后搬移&am…

Java设计模式:Callback

介绍 回调&#xff08;Callback&#xff09;是一种设计模式&#xff0c;在这种模式中&#xff0c;一个可执行的代码被作为参数传递给其他代码&#xff0c;接收方的代码可以在适当的时候调用它。 在真实世界的例子中&#xff0c;当我们需要在任务完成时被通知时&#xff0c;我…

【Linux】从零开始学习Linux基本指令(一)

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;Linux入门 &#x1f525;该文章主要了解Linux操作系统下的基本指令。 目录&#xff1a; ⌛️指令的理解⏳目录和文件的理解⏳一些常见指令✉…

C++入门篇11 模板进阶

一、非类型模板参数 模板参数分为类型形参和非类型形参 类型形参&#xff1a;出现在模板参数列表里&#xff0c;跟在class/typename之后的参数类型名称非类型参数&#xff1a;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中可将参数当作常量来使用 …

SRAM电路设计

RAM是随机存取存储器&#xff08;random access memory&#xff09;&#xff0c;是计算机内部存储器中的一种&#xff0c;也是其中最重要的&#xff0c;计算机和手机中一般把其叫做&#xff08;运行&#xff09;内存&#xff0c;它的速度要比硬盘快得多&#xff0c;所以用运行程…

设计师都应该知道的事:极简主义家具该怎么去用

这座房子有黑暗而沉重的特征&#xff0c;包括棕色和白色的马赛克浴室瓷砖&#xff0c;弯曲的锻铁壁灯和土黄色的威尼斯石膏墙。但由于房屋与他们的风格相去甚远&#xff0c;白色&#xff0c;干净和简约&#xff0c;接下来我们就着这个方向去帮助房主进行改造。 她解释说&#x…

uniapp 小程序实现图片宽度100%、高度自适应的效果

因为image组件默认是有宽度跟高度的&#xff0c;所以这个高度不怎么好写 通过load事件来控制图片的高度 话不多说&#xff0c;直接上代码&#xff0c; <image class"img" src"/static/image.png" :style"{ height: imgHeight px }"mode&q…

【linux】E45: ‘readonly‘ option is set (add ! to override)

vim 编辑文件保存时 E45:设置了“只读”选项&#xff08;添加&#xff01;以覆盖&#xff09; 输入&#xff1a; wq! 提示 "/etc/my.cnf" E212: Cant open file for writing 依然是没有权限&#xff1a; 解决一&#xff1a; 切换用户&#xff1a; su root 解…

Elastic Cloud v.s. Zilliz Cloud:性能大比拼

Elastic Cloud v.s. Zilliz Cloud:性能大比拼 Zilliz 经常会收到来自开发者和架构师的提问:“Zilliz Cloud 和 Elastic Cloud 比起来,谁进行向量处理能力比较强?” 诸如此类的问题很多,究其根本,大都是开发者/架构师在为语义相似性检索系统进行数据库选型时缺少决策依据有…

网络层:常见的面试题和答案

1、什么是IPv4和IPv6&#xff1f;它们有什么区别&#xff1f; 答&#xff1a;IPv4是32位的IP地址格式&#xff0c;而IPv6是128位的IP地址格式。IPv4地址空间有限&#xff0c;而IPv6地址空间更大&#xff0c;可以提供更多的地址。 2、说说 HTTP 和HTTPS 的区别&#xff1f; H…

【C++】适配器模式 - - stack/queue/deque

目录 一、适配器模式 1.1迭代器模式 1.2适配器模式 二、stack 2.1stack 的介绍和使用 2.2stack的模拟实现 三、queue 3.1queue的介绍和使用 3.2queue的模拟实现 四、deque&#xff08;不满足先进先出&#xff0c;和队列无关&#xff09; 4.1deque的原理介绍 4.2dequ…

基于matomo实现业务数据埋点采集上报

matomo是一款Google-analytics数据埋点采集上报的平替方案&#xff0c;可保护您的数据和客户的隐私&#xff1b;正如它官网的slogan: Google Analytics alternative that protects your data and your customers privacy; 该项目源码开源免费&#xff0c;支持私有化部署&#x…

零基础Linux_17(进程间通信)VSCode环境安装+进程间通信介绍+pipe管道mkfifo

目录 1. VSCode环境安装 1.1 使用VSCode 1.2 远程链接到Linux机器 1.3 VSCode调试 2. 进程间通讯介绍 2.1 进程间通讯的概念和意义 2.2 进程间通讯的策略和本质 3. 管道 3.1 管道介绍 3.2 匿名管道介绍 3.3 匿名管道示例代码 3.3.1 建立管道的pipe 3.3.2 匿名管道…

YOLOv5算法改进(10)— 如何去添加多层注意力机制(包括代码+添加步骤+网络结构图)

前言:Hello大家好,我是小哥谈。注意力机制是近年来深度学习领域内的研究热点,可以帮助模型更好地关注重要的特征,从而提高模型的性能。注意力机制可被应用于模型的不同层级,以便更好地捕捉图像中的细节和特征,这种模型在计算资源有限的情况下,可以实现更好的性能和效率。…

二十一、【文本工具组】

文章目录 横排文本工具字符选项卡段落文字 横排文本工具 需要注意的是一些不是免费的商业字体&#xff0c;一定不要拿去使用&#xff0c;否则后边很容易会受到法律索赔。 在制作海报等一些图形时&#xff0c;需要经常用到文本工具我们需要对文本进行变形&#xff0c;分段&…