你知道函数栈帧的创建和销毁吗?

news2024/10/5 13:31:56

在这里插入图片描述

文章目录

  • 前言
  • 观图有感
  • 一、概述
  • 二、寄存器
  • 三、汇编指令
  • 四、函数栈帧的创建
    • 4.1 main函数栈帧的创建
      • push ebp
      • mov ebp,esp
      • sub esp,0E4h
      • push ebx / esi /edi
      • lea edi,[ebp-24h] 、mov ecx,9、mov eax,0CCCCCCCCh、rep stos dword ptr es:[edi]
      • main函数中变量的创建
    • 4.2 在main函数中调用Add函数
      • 传参
      • 进入Add函数
      • Add函数中变量Z的创建
      • z=x+y
  • 五、函数栈帧的销毁
  • 总结

前言

在前面的学习中,可能会出现许多疑惑:
1、局部变量是怎么创建的?
2、函数是怎么传参的?
3、函数调用是怎么做到的?
4、函数调用结束后是怎么返回的?

希望读者在看完小编的文章,对一系列问题会有所掌握

观图有感

你去野外烧烤,并为此创建了一个待办事项清单——一叠便条。

在这里插入图片描述

将想到的烧烤食物写在便条上,一个食材一个便条,最先想到的食材写在便条上后,放在最下面,依次往上放,最后想到的写在便条上后,放在最上面。

之后,在烧烤的时候,从上往下拿,拿出来的表示你已经在烧烤了,可以将它删去。

一叠便条要简单得多:插入的待办事项放在清单的最前面;读取待办事项时,你只读取最上面的那个,并将其删除。因此这个待办事项清单只有两种操作:压入(插入)和弹出(删除并读取)。

在这里插入图片描述

在这里插入图片描述

这种数据结构称为。栈是一种简单的数据结构,之前学函数的时候我们一直在使用它,却没有意识到!

一、概述

函数栈帧是在内存中的栈区为被调函数开辟的一块空间,里面用来存放该函数中定义的变量等东西,当函数运行完毕栈帧将被销毁。

可以想象成洗盘子,最先吃完的人将盘子放在最下面,后面吃完的人依次将盘子叠放在前一个的上面。于是,最后吃完的人的盘子就在最上面,也就是最先洗。

Push(入栈):为栈增加一个元素
Pop (出栈): 从栈中取出一个元素

二、寄存器

寄存器是中央处理器内用来暂存指令、数据和地址的电脑存储器。寄存器的存贮容量有限,读写速度非常快。在计算机体系结构里,寄存器存储在已知时间点所作计算的中间结果,通过快速地访问数据来加速计算机程序的执行。
–百科

NameFunction
eax“累加器”, 用来存放函数的返回值
ebx"基地址"寄存器,可作为储存器指针来使用, 在内存寻址时存放基地址
ecx计数器, 在循环和指针操作时,要用它来控制循环次数
edx"数据寄存器’,在进行乘、除法运算时,可作为默认的操作数参数参与运算
esp栈指针寄存器,存放函数栈顶地址
ebp帧指针寄存器,存放函数栈底地址

esp和ebp这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的
在本节中,主要了解这俩寄存器

三、汇编指令

DirectivesFunction
push x将x压入栈中
pop x将x弹出栈中
mov a, b将b赋值给a,即b指向a
sub anum a的值减去num,即a向低地址移动
lea(load effective adress)加载有效地址(在示例中理解)

四、函数栈帧的创建

所有函数的调用都会在内存里面的栈区创建函数栈帧,包括main函数。

以下面一个详细的代码,描述函数栈帧的创建
本次代码是在 vs 2013 里面实现的,版本越低,可以更好展示

#include <stdio.h>
int Add(int x, int y)
{
	int z = x + y;
	return z;
}

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	return 0;
}

按F10,进行调试
在这里插入图片描述

4.1 main函数栈帧的创建

C语言所对应的汇编代码
在这里插入图片描述

int main()
{
  push        ebp           //将ebp压入栈中
  mov         ebp,esp       //将esp赋值给ebp,即将esp移动到ebp的位置
  sub         esp,0E4h      //将esp向低地址移动0E4h个字节的位置
  push        ebx           //(我们不要管)
  push        esi           //(我们不要管)
  push        edi           //(我们不要管)
  lea         edi,[ebp-24h]  //将[ebp-24h]存入edi中
  mov         ecx,9          //将9存入ecx中
  mov         eax,0CCCCCCCCh  //将0CCCCCCCCh存入eax中
  rep stos    dword ptr es:[edi]  //将edi的值对应的地址处开始,将高于该地址共ecx个单位的值置为0CCCCCCCCh
  
	int a = 10;
  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
  mov         dword ptr [ebp-14h],14h  
	int c = Add(a, b);
  mov         eax,dword ptr [ebp-14h]  
  push        eax  
  mov         ecx,dword ptr [ebp-8]  
  push        ecx  
  call        011C10B4  
  add         esp,8  
  mov         dword ptr [ebp-20h],eax  
	return 0;
  xor         eax,eax  
}
  pop         edi  
  pop         esi  
  pop         ebx  
  add         esp,0E4h  
  cmp         ebp,esp  
  call        011C1235  
  mov         esp,ebp  
  pop         ebp  
  ret  

看着有点麻烦,不过对着汇编语言,可以仔细研究一番。

首先看main函数

在这里插入图片描述

栈使用空间是由高地址到低地址
正在调用哪个函数,esp和ebp就维护哪个函数,在这里,我们调用的是main函数,那么就维护main函数。

通过 __tmainCRTStartup 函数调用main函数,所以要创建好__tmainCRTStartup 的栈帧

push ebp

在这里插入图片描述

push ebp就是把__mainCRTStartup 函数栈底的地址压栈,ebp的值压入后,esp指针会上移一位

mov ebp,esp

在这里插入图片描述

sub esp,0E4h

在这里插入图片描述

push ebx / esi /edi

在这里插入图片描述

lea edi,[ebp-24h] 、mov ecx,9、mov eax,0CCCCCCCCh、rep stos dword ptr es:[edi]

在这里插入图片描述

main函数中变量的创建

在这里插入图片描述
在这里插入图片描述

4.2 在main函数中调用Add函数

在这里插入图片描述

	int a = 10;
  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
  mov         dword ptr [ebp-14h],14h  
	int c = Add(a, b);
  mov         eax,dword ptr [ebp-14h]  //把ebp-14h这个地址里面存放的值(也就是20)赋值给eax
  push        eax    //push(压栈)eax
  mov         ecx,dword ptr [ebp-8]  //把ebp-8这个地址里面存的值(也就是10)赋值给ecx
  push        ecx      //push(压栈)ecx
  
  
  call        011C10B4  
  add         esp,8  
  mov         dword ptr [ebp-20h],eax  

传参

经过这两次压栈后,esp指针指向的地址减小8个字节(一次减小4个字节,也就是1个整型)

在这里插入图片描述

进入Add函数

call 011C10B4 执行这条语句,在执行这条语句时,我们需要按键盘上的F11(笔记本电脑可能需要按Fn+F11)。

在按完F11后,我们就进入了Add函数内部,而且还会发现,esp指针还向上移动了4个字节

在这里插入图片描述
003F1450 是call指令的下一条指令的地址。

为什么要将call指令的下一条指令的地址存起来呢??
是因为在Add函数调用结束的时候,需要返回继续执行call指令的下一条指令,所以在执行call指令的时候要将call下一条指令的地址存起来,在Add函数调用结束的时候,就能根据存的地址找到call指令的下一条指令,从而让程序可以继续执行。

紧接着继续按F11,就真正来到了Add函数里面

剩下的过程其实和在调用main函数的动画演示是一样的,不再做过多演示。

Add函数中变量Z的创建

此过程和main函数中变量a,b,c创建的过程是一样的

在这里插入图片描述

z=x+y

int z = x + y;
  mov         eax,dword ptr [ebp+8]  //把ebp+8这个地址里面存储的值放到eax里
  add         eax,dword ptr [ebp+0Ch]  //把ebp+0Ch这个地址里面存储的值加到eax里面去
  mov         dword ptr [ebp-8],eax  //把eax里面的值存到ebp-8这个地址里面(变量z的地址)
 return z;
  mov         eax,dword ptr [ebp-8]

五、函数栈帧的销毁

  pop         edi  
  pop         esi  
  pop         ebx  
  add         esp,0CCh  

会发现pop指令,代表出栈
将edi,esi和ebx弹出栈

add esp,8这条指令,该指令的执行结果是让esp指针指向的地址加8
在这里插入图片描述
mov dword ptr [ebp-20h],eax指令。执行结果是:把eax里面存的值(30)赋值给ebp-20h所指向的这块空间,其实就是变量c的存储空间

执行这条指令之前ebp-20h所指向的这块空间存的值是0,在执行完这条指令之后,ebp-20h所指向的这块空间里面存的就是1e(十进制下的30)。并且可以看出ebp-20h和&c的值相同,说明他们指向同一块空间——变量c的存储空间.

总结

本节涉及一些数据结构的内容,但是为了解决心中的疑惑,我们可以了解一下。
如有误,欢迎指正!!

在这里插入图片描述

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

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

相关文章

Focal and Global Knowledge Distillation for Detectors(CVPR 2022)原理与代码解析

paper&#xff1a;Focal and Global Knowledge Distillation for Detectors official implementation&#xff1a;https://github.com/yzd-v/FGD 存在的问题 如图1所示&#xff0c;前景区域教师和学生注意力之间的差异非常大&#xff0c;背景区域则相对较小。此外通道注意力…

【错误记录】Uncaught SyntaxError: Not available in legacy mode

错误记录&#xff1a;Uncaught SyntaxError: Not available in legacy mode 错误描述&#xff1a;在vite脚手架项目当中&#xff0c;使用vue-i18n插件进行国际化多语言时&#xff0c;报错 解决方案&#xff1a; 在引入vue-i18n 处&#xff0c;添加 legacy: false 如果对项目…

玩转Vue3:计算属性和监视属性深度解析

计算属性computed Vue中的计算属性是一种特殊的属性&#xff0c;它可以根据依赖的数据动态计算并返回结果。计算属性的值是通过getter函数计算得到的&#xff0c;当依赖的数据发生变化时&#xff0c;计算属性会自动重新计算并更新视图。计算属性具有缓存机制&#xff0c;只有当…

SSM的知识点考试系统java在线问答试卷管理jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 SSM的知识点考试系统 系统1权限&#xff1a;管理员 …

把大模型装进手机,分几步?

点击关注 文 | 姚 悦 编 | 王一粟 大模型“跑”进手机&#xff0c;AI的战火已经从“云端”烧至“移动终端”。 “进入AI时代&#xff0c;华为盘古大模型将会来助力鸿蒙生态。”8月4日&#xff0c;华为常务董事、终端BG CEO、智能汽车解决方案BU CEO 余承东介绍&#xff0c…

【计算机网络】UDP服务器实现网络聊天室

前言 上一篇文章我们简单了解了一下什么是套接字编程&#xff0c;这篇文章我们利用UDP套接字来实现一个简单的网络聊天室。 编写UDP套接字服务器 成员变量 // 1. socket的id&#xff0c;相当于文件id int _sock; // 2. port uint16_t _port;// 3 一个线程负责收放消息&…

JUC并发、JVM相关

文章目录 JUC并发synchronized锁对象底层原理 synchronized锁升级reentrantlock公平锁和非公平锁可重入锁 / 递归锁 死锁死锁产生条件如何排查死锁?如果解决死锁&#xff1f; LockSupport与中断机制中断机制中断相关的三大API如何中断运行中的线程&#xff1f; LockSupportLoc…

【C++】C++11--- 线程库及详解lock_guard与unique_lock

目录 一、thread类的介绍二、线程函数参数三、 原子性操作库四、lock_guard与unique_lock4.1、mutex的种类4.2 lock_guard4.3 unique_lock 一、thread类的介绍 在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如**windows和linux下各有自己…

【css】属性选择器

有些场景中需要在相同元素中获取具有特定属性的元素&#xff0c;比如同为input&#xff0c;type属性有text、button&#xff0c;可以通过属性选择器设置text和button的不同样式。 代码&#xff1a; <style> input[typetext] {width: 150px;display: block;margin-bottom…

自动配置要点解读

目录 要点1&#xff1a;什么是自动配置&#xff1f; 要点2&#xff1a;配置文件与默认配置 要点3&#xff1a;自动配置设置思想来源 要点4&#xff1a;spring.factories文件作用 要点5&#xff1a;自动配置的核心 本文只对自动配置的思想进行基本的解读&#xff0c;不涉…

21、p6spy输出执行SQL日志

文章目录 1、背景2、简介3、接入3.1、 引入依赖3.2、修改database参数&#xff1a;3.3、 创建P6SpyLogger类&#xff0c;自定义日志格式3.4、添加spy.properties3.5、 输出样例 4、补充4.1、参数说明 1、背景 在开发的过程中&#xff0c;总希望方法执行完了可以看到完整是sql语…

通用人工智能操作系统

随着科技的飞速发展&#xff0c;人工智能已经成为了当今世界最热门的技术领域之一。从智能手机、自动驾驶汽车到智能家居系统&#xff0c;人工智能技术已经渗透到了我们生活的方方面面。然而&#xff0c;尽管人工智能在很多领域取得了显著的成果&#xff0c;但它仍然存在一些局…

matplotlib+tkinter实现一个简单的绘图系统

文章目录 封装成类布局实现绘图功能 绘图系统系列&#xff1a;将matplotlib嵌入到tkinter 封装成类 在理解matplotlib嵌入到tkinter中的原理之后&#xff0c;就已经具备了打造绘图系统的技术基础&#xff0c;接下来要做的&#xff0c;就是做一个较有可读性的绘图类&#xff0…

Java异常体系总结(下篇)

目录 1. 异常处理的三种方法 1.1 JVM 默认处理异常 1.2 通过 try...catch...自己处理异常 1.3 使用 throws和throw 抛出异常 1.3.1 使用 throws 抛出异常 1.3.2 使用 throw 抛出异常 2. try...catch.. 捕获到异常之后代码的执行顺序&#xff1f; 3. try...catch... 相关…

Mysql进阶(中) -- 索引

索引上部分 -> Mysql进阶(上) -- 存储引擎&#xff0c;索引_千帐灯无此声的博客-CSDN博客 &#x1f442; 爸爸妈妈 - 王蓉 - 单曲 - 网易云音乐 &#x1f448;目录看左栏 目录 &#x1f33c;索引 &#x1f43b;性能分析 - show profiles &#x1f43b;性能分析 - exp…

Cocos 适配 HarmonyOS NEXT,亮相 HDC2023,携手华为共筑鸿蒙生态!

HDC 2023 8月4-6日&#xff0c;作为华为合作伙伴&#xff0c;Cocos 引擎应邀参加了华为开发者大会 2023 - HDC 2023 暨 HarmonyOS 4 发布会&#xff0c;并获得了【鸿蒙生态能力共创奖】。 8月5日&#xff0c;在华为开发者大会&#xff08;HDC.Together&#xff09;游戏服务论坛…

SpringBoot系列---【使用jasypt把配置文件密码加密】

使用jasypt把配置文件密码加密 1.引入pom坐标 <dependency><groupId>com.github.ulisesbocchio</groupId><artifactId>jasypt-spring-boot-starter</artifactId><version>3.0.5</version> </dependency> 2.新增jasypt配置 2.1…

HCIP-linux知识

linux安装教程参考&#xff0c;https://blog.51cto.com/cloudcs/5245337 yum源配置 本地yum源配置&#xff1a; 8版本配置&#xff1a;将光盘iso挂载到某个目录&#xff0c;/dev/cdrom是/dev/sr0软链接&#xff0c;# mount /dev/cdrom /mnt&#xff0c;# ls /mnt AppStream B…

Elastic:linux设置elasticsearch、kibana开机自启

0. 引言 每次启动服务器都要手动启动es服务&#xff0c;相当之不方便&#xff0c;为此&#xff0c;书写一个脚本&#xff0c;实现es、kibana的开机自启 1. 原理 首先任何服务要实现开机自启&#xff0c;都可分为如下三步&#xff1a; 1、在/etc/init.d目录下创建启动、关闭服…

跳表与Redis

跳表原理 跳表是Redis有序集合ZSet底层的数据结构 首先有一个头结点 这个头结点里面的数据是null 就是他就是这个链表的最小值 就算是Math.Min也比它大 然后我们新建一个节点的时候是怎么操作的呢 先根据参数(假如说是5)创建一个节点 然后把它放在对应位置 就是找到小于他的最…