【C++庖丁解牛】函数栈帧的创建与销毁

news2024/11/17 2:22:59
🍁你好,我是 RO-BERRY
📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识
🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油

在这里插入图片描述


目录

  • 1. 寄存器
  • 2. ebp和esp是如何对堆栈进行维护的,mian函数栈帧如何创建
    • 2.1 push ebp
    • 2.1 move ebp esp
    • 2.2 sub esp, 0E4h
    • 2.3 push exb
    • 2.4 push exi
    • 2.5 push edi
    • 2.6 push edi
    • 2.6 lea edi,[ebp+FFFFFF1Ch]
    • 2.7 mov ecx,39h 以及mov eax,0CCCCCCCCh
    • 2.8 rep stos dword ptr es : [edi]
  • 3. 局部变量栈帧的创建
    • 3.1 对a分配空间
    • 3.2 对b分配空间
    • 3.3 对c分配空间
  • 4. 函数的调用


1. 寄存器

寄存器我们了解过有eax、ebx、ecx、edx、ebp、esp等等

本节的重点是我们的ebp、esp这两个寄存器,这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的

每一个函数的调用都要创建一块空间,这个空间创建在栈区上。
在这里插入图片描述

  • esp则为我们的栈顶指针
  • ebp就是我们的栈底指针

esp (EStack Pointer):栈顶指针,它指向当前栈帧中的栈顶位置。每当一个新的函数被调用时,esp会动态地更新以跟踪新参数的压入和局部变量的分配。当函数执行完毕,esp会回退到释放这些资源的位置。

ebp (EBase Pointer):栈底指针,又称为基指针,它通常用于存储当前函数的帧信息,比如函数的局部变量、参数和其他数据。ebp在函数开始时被设置为栈顶减去函数头部所需的空间,这样就可以作为访问栈中所有局部变量的基地址。

在函数调用过程中,esp和ebp经常被用来进行帧布局的操作,例如保存旧的esp值,然后将新的esp指向新的函数参数,而 ebp则保持不变,作为固定不变的局部变量基址。当函数返回时,esp通常会恢复到原来的值,释放栈帧,而ebp也会回到正确的基址,以便后续函数继续正确地访问局部变量。


2. ebp和esp是如何对堆栈进行维护的,mian函数栈帧如何创建

其实main函数也是被另一个内部系统函数调用的
具体调用为

mainCRTStartup —> __tmainCRTStartup —> main
前两个函数都是系统里的调用函数


程序在一开始运行时,ebp以及esp是维护__tmainCRTStartup的函数栈帧的
在这里插入图片描述

栈空间的使用是由高地址往低地址使用的

在执行main函数的时候就会往上进行创建栈帧

2.1 push ebp

我们将写的代码进行反汇编的操作,看到程序底层的汇编代码可以看到第一步就是对于ebppush操作

push ebp 的作用是将 ebp 的值压栈(Push the value of ebp onto the stack),这样在函数执行过程中,ebp 就不会被其他操作覆盖,保持对函数调用上下文的引用。

当函数开始执行时,push ebp 让 ebp 保存当前堆栈帧的状态,然后 mov ebp, esp esp(栈指针)的内容赋给 ebp,从而esp指向新的栈顶,用来作为新创建的局部变量的内存地址。这样做有助于维护函数调用的上下文,便于后续访问和管理局部变量、参数等。
在这里插入图片描述

也就是说将ebp此时的值存入栈中,以便后面进行查找,在此时就是__tmainCRTStartup的ebp值

当我们在没有执行push操作时,esp与ebp的值如下
在这里插入图片描述

在我们执行后esp的值从后两位为a8,变为了a4
在这里插入图片描述

这也说明了esp被压入栈。
此时的esp则往低地址走了几步,现在指向的就是__tmainCRTStartup的ebp的地方了
在这里插入图片描述


2.1 move ebp esp

执行了 push ebp之后,第二步就是move ebp esp

在这里插入图片描述

move ebp esp 的操作意味着将 ebp 的值赋给 esp。这种操作经常发生在函数返回或异常处理时,因为当函数结束时,可能需要清空堆栈,将esp回退到栈帧之前的状态,以便为下一次函数调用腾出空间。在清理过程中,ebp 通常会被用来保存堆栈的原有状态,然后将其位置替换到 esp,这样就可以清除函数调用时的信息。

也就是ebp与esp都指向__tmainCRTStartup的ebp
在这里插入图片描述


2.2 sub esp, 0E4h

第三步为sub esp, 0E4h
在这里插入图片描述

sub esp, 0E4h是一个指令组合,它代表"从堆栈指针ESP(通常用于跟踪函数调用时的局部变量和参数)中减去0E4个字节(16进制的0E4等于十进制的220)"。这个操作常用于函数调用或内存分配,可能是为了为新的局部变量分配空间或者调整堆栈布局。

具体来说:

  • sub是"subtract"的缩写,即减法操作。
  • esp是堆栈指针,它指向栈顶,减去一个数意味着将栈顶地址向下移动。
  • 0E4h是一个16进制数,转换成十进制就是220,所以实际上是将栈顶的220个字节移除或压入栈中。

这段空间其实就是为main函数预留的一段空间
在这里插入图片描述
在执行完这个操作后
在这里插入图片描述


2.3 push exb

在这里插入图片描述

push exb顾名思义也是将exb此时的数据存储到栈中

在这里插入图片描述


2.4 push exi

在这里插入图片描述
将esi此时的数据存储到栈中
在这里插入图片描述


2.5 push edi

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


2.6 push edi

在这里插入图片描述

在这里插入图片描述


2.6 lea edi,[ebp+FFFFFF1Ch]

在这里插入图片描述

lea edi, [ebp+FFFFFF1Ch] 是一条x86汇编指令,其中 “lea” 是load effective address(有效地址加载)的缩写,它用于计算并存储一个内存地址到寄存器edi中。在这个指令中:

  • edidestination operand(目标操作数),通常用于存放计算出的内存地址。
  • [ebp+FFFFFF1Ch]source operand(源操作数),它使用基址加变址寻址方式。ebp 是基指针(base pointer),用于访问栈帧中的数据。加上偏移量 FFFFFF1Ch,意味着从栈帧的当前位置向上偏移0xFFFFFF1Ch处的内存位置。

这条指令的作用是将栈上某个特定位置的地址赋值给edi,这个位置通常是函数调用时为了后续操作需要而存储的数据地址。在分析程序代码时,这可能对应于函数的局部变量、参数或其他动态分配的数据结构的地址。

在这里其实就是mian函数的栈顶地址给了edi寄存器


2.7 mov ecx,39h 以及mov eax,0CCCCCCCCh

在这里插入图片描述

  1. mov ecx, 39h: 这行指令将立即数 39h(十六进制,等于十进制的 57)传送到名为 ecx 的寄存器中。ecx 通常用于索引或循环计数。在这里,ecx 被设置为一个特定的值,可能用于控制某种循环次数或者作为数组操作的下标。

  2. mov eax, 0CCCCCCCCh: 这行指令将十六进制数值 0CCCCCCCCh (十进制的 -1073741821) 移动到 eax 寄存器。eax 在x86架构中是一个常用的通用寄存器,常用于存储操作数。 0CCCCCCCCh是一个特殊的值,有时在某些情况下用于测试内存是否已初始化(因为它几乎不会出现在正常的初始化中)。

相当于代码

ecx = 39h
eax = 0CCCCCCCCh
对这两个寄存器赋值

2.8 rep stos dword ptr es : [edi]

在这里插入图片描述

dword ptr es:[edi] ,edi(EAX的低16位)是一个寄存器,指向存储的起始位置,es(额外段寄存器)指定数据段,ptr表示是按字节偏移地址。因此,这整个指令组合的意思是:

edx寄存器开始,重复执行存储操作,每次将源操作数中的两个字节写入es段的当前指定位,然后地址指针edi递增指向下一个存储位置,直到所有数据都被写入。

也就是将ecx39h个空间全部写入成eax0CCCCCCCCh,相当于给我们开辟的空间进行初始化

在这里插入图片描述


以上这个部分main函数的函数栈帧就已经创建完成了


3. 局部变量栈帧的创建

3.1 对a分配空间

在这里插入图片描述
这里就是将a放入ebp-8的位置上去,a为int类型四个字节
在这里插入图片描述


3.2 对b分配空间

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


3.3 对c分配空间

在这里插入图片描述

在这里插入图片描述


4. 函数的调用

在这里插入图片描述
这里的步骤就是

  1. 将b的值放入eax中,再将eax压栈,放入栈中
  2. 将a的值放入ecx中,再将ecx压栈,放入栈中
  3. call指令调用add函数,call指令前面的地址为call指令的下一条地址
    在这里插入图片描述
  4. 跳到add函数准备栈帧,并执行add函数
    在这里插入图片描述
    最后通过函数的调用返回结果,最后得到结果

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

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

相关文章

【前端】[vue3] [uni-app] 组件样式击穿:deep

我是在开发uni-app时测试的思路,大家可以借鉴一下。 我这边测试的是uni组件,但是我觉得即便你用element-plus之类的,样式击穿的思路都相同。 我自定义了一个全局样式scss文件,并引入到了项目中。(如图) 利用vue3 中的 deep 方式…

2024届应届生突破1179万人,如何瞄准AI行业,获得高薪职业前景

应届生如何开启AI行业的职业生涯? ©作者|Zhongmei 来源|神州问学 引言 人工智能和数据科学几乎可以在任何产生数据的场合中发挥作用,因为它们开辟了一种全新的范式——让机器能够从数据中提炼出知识。自2011年人工智能走出实验室的门槛&#xff0c…

Sapphire开发日志 (十一) 登陆注册功能

任务介绍 本周实现登陆注册功能。 由于本项目主要关注在数据集的相关操作,所以对于用户的登录注册验证没有必要采取完整的一套手机号(邮箱)验证机制,简单的实现一个注册和登录功能即可。 背景效果 首先展示效果 整体背景页面和…

【事件总线】EventBus

文章目录 概述如何使用如何发布消息如何进行消息监听 来源 概述 事件总线是对发布-订阅模式(观察者)的一种实现,是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需要相互依赖,达到一种解耦的目的。 …

喜讯:ISO年度审核通过!

在数字化时代,质量是我们不变的追求。近日,矩阵起源迎来了一个值得庆祝的时刻——三项ISO体系年度考核顺利通过!分别为:ISO9001 质量管理体系标准认证、ISO20000信息技术服务管理体系认证及ISO27001 信息安全管理体系认证。 ISO标…

摘苹果-第13届蓝桥杯省赛Python真题精选

[导读]:超平老师的Scratch蓝桥杯真题解读系列在推出之后,受到了广大老师和家长的好评,非常感谢各位的认可和厚爱。作为回馈,超平老师计划推出《Python蓝桥杯真题解析100讲》,这是解读系列的第88讲。 摘苹果&#xff0…

品牌出海!独立站不仅是方向,而是成为跨境电商主流了

近些年,随着跨境电商3.0品牌出海和本土化时代的到来,独立站成为品牌出海的必备渠道和关键胜负手! 搭建独立站作为品牌本土化的最好展示载体,独立的品牌包装与产品页面,能够更好讲述品牌故事,因而得以快速发…

fvcore库的一些功能和使用

目录 一、安装fvcore库 二、使用 fvcore是Facebook开源的一个轻量级的核心库,它提供了各种计算机视觉框架中常见且基本的功能。其中就包括了统计模型的参数以及FLOPs等。 项目地址:fvcore 一、安装fvcore库 pip install fvcore 二、使用 1、计算模…

Debian12中搭建TiddlyWiki服务并进行配置

一、Node.js 环境安装 apt update & apt install nodejs npm ## 查看版本 node -v npm -v二、安装Tiddlywiki npm install -g tiddlywiki## 查看版本号 tiddlywiki --version 三、配置并运行 tiddlywiki ## 在/home 目录自动创建Testwiki文件夹,作为wiki的存…

项目实践---Windows11中安装Zookeeper/Hadoop/Hive的部分问题解决

一.Hadoop与Hive兼容版本选择 正常来说,Hadoop与Hive版本不兼容会出现很多问题导致hive安装失败,可以先确定HIve的版本,比如:要用Hive3.1.2版本,该如何确定使用Hadoop的版本呢,需要我们在hive源码中找到对…

自学鸿蒙HarmonyOS的ArkTS语言<四>@Builder自定义构建函数

作用:可以提取重复的ui, 在buil()中调用 【官方文档传送门】 一、用在自定义组件内 自定义组件内可以有一个或多个Builder只能在所属组件的builder方法和其他Builder中调用,组件外不能调Builder中的this指当前所属组件,组件的状态变量可以在…

LeetCode 热题100 --双指针

双指针 b站UP主蜜糖:由于数据特征的有序性(大小或者正负),所以可以证明当前节点一定是优于过往节点,从而可以通过数据的维度数量的指针,逐步的迭代收敛最终找到最优解。 283.移动零 相关标签 :…

计算机网络 —— 应用层(电子邮件)

计算机网络 —— 应用层(电子邮件) 电子邮件发送电子邮件的过程SMTP特性工作流程 电子邮件格式MIME关键组件工作方式 POP/IMAPPOP(邮局协议)IMAP(因特网邮件访问协议) 基于万维网的电子邮箱特点优势常见的基…

营销复盘秘籍,6步法让你的活动效果翻倍

在营销的世界中,每一次活动都是一次探险,而复盘就是探险后的宝藏图,指引我们发现问题、提炼经验、优化策略。 想要学习如何复盘,只要了解以下复盘六大步骤,即可不断总结,逐渐走向卓越。 第一步&#xff1…

lvgl v8.3 windows仿真

1,下载安装codeblocks20.03 https://zenlayer.dl.sourceforge.net/project/codeblocks/Binaries/20.03/Windows/codeblocks-20.03mingw-setup.exe?viasf1 2,下载代码 git clone --recurse-submodules https://github.com/lvgl/lv_port_win_codeblock…

fairseq (Facebook AI Research) 包

0. Abstract 最近在看一个用 RNNs 网络做 Translation 任务的程序, 关于数据处理部分, 主要用到工具包 sentencepiece 和 fairseq, 前者主要是对文本进行分词处理, 后者则是对已分词的文本进行二进制化和快速加载. 包越方便使用, 就说明包装得越狠, 也就越令人一头雾水, 本文简…

虚拟机拖拽文档造成缓存过大

查看文件夹大小:du -h --max-depth1 缓存位置:~/.cache/vmware/drag_and_drop 删除:rm -fr ~/.cache/vmware/drag_and_drop 释放了3GB

Javascript中的this关键字指向

this关键字介绍 不同情况下的this 1.对象调用方法中的this 2.在全局使用this(单独使用) 3.函数中的this 4.函数严格模式下 5.事件中的this 6.构造函数中的this 7.箭头函数没有this call()、apply()、bind() 的用法 this关键字介绍 面向对象语言中 this 表示当前对象…

JavaScript中的内置对象

// 用法2:参数:指定的时间的字符串 创建一个指定的时间 // new Date(‘2018-12-12 12:00:00’) var date1 new Date(‘2018-12-12 12:00:00’); console.log(date1); // 用法3: 参数可以是年月日时分秒 月份从0开始 // var date2 new …

基于Java实验室课程管理系统设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码数据库🌟 感兴趣的可以先收藏起来,…