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

2024-06-20 21:36

本文主要是介绍【C++庖丁解牛】函数栈帧的创建与销毁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🍁你好,我是 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函数
    在这里插入图片描述
    最后通过函数的调用返回结果,最后得到结果

这篇关于【C++庖丁解牛】函数栈帧的创建与销毁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1079315

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多