首页 > 其他分享 >函数栈帧的创建与销毁

函数栈帧的创建与销毁

时间:2024-08-23 16:26:37浏览次数:8  
标签:00 销毁 函数 int mov ebp 栈帧

目录

1.什么是函数栈帧

2.了解函数栈帧的好处

3.函数栈帧的创建与销毁

3.1什么是栈

3.2 认识相关寄存器和汇编指令

3.3解析函数栈帧的创建与销毁

3.3.1预备知识

3.3.2 函数的调用堆栈

3.3.3 准备环境

3.3.4 转到反汇编


1.什么是函数栈帧

我们在写C语言代码的时候,经常会把一个独立的功能抽象为函数,所以C程序是以函数为基本单位的。函数栈帧是内存中存储执行特定函数所需信息的一块区域就是函数在使用过程中在栈区上所开辟的空间。它包含了函数执行过程中所需的各种数据,如局部变量,返回地址,传递给函数的参数等。

2.了解函数栈帧的好处

加深我们对栈区以及函数的理解

  • 局部变量是怎么创建的?
  • 为什么局部变量的值是随机值?
  • 函数是怎么传参的?传参的顺序是怎样的?
  • 形参和实参是什么关系?
  • 函数调用是怎么做的?
  • 函数调用结束后怎么返回

当你将这篇文章看完之后你就会得到答案

那我们开始进入函数栈帧的海洋中

3.函数栈帧的创建与销毁

3.1什么是栈

这里就需要介绍内存中是如何划分区域

由图可以看出从低地址往高地址依次分配:

  • 代码区(code segment):你写的代码在这个区域中,这个区域的代码可以共享,用来节省内存空间,代码区通常是只读的
  • 常量区(constant):常量区是用来存储字符串常量,数字常量,#define的常量和其他静态常量数据,其内容在运行时不能被修改
  • 静态区(全局区)(static):用于存放全局变量和静态变量,存放在静态区的变量,整个程序运行期间都存在
  • 堆区(heap):用于动态内存分配,通常用malloc手动申请和释放
  • 栈区(stack):栈区是由编译器自动分配和释放的内存区域,用于存放局部变量,函数,函数参数和返回值。栈区的内存使用规则是:先使用高地址在使用低地址

这次我们的主角是栈区(stack)

3.2 认识相关寄存器和汇编指令

相关寄存器

  • eax:通用寄存器,保留临时数据,常用于返回值
  • ebx:通用寄存器,保留临时数据
  • ebp:栈底寄存器
  • esp:栈顶寄存器
  • eip:指令寄存器,保存当前指令的下一条指令的地址

虽然有很多寄存器但是我们需要记住两个即可esp,ebp

相关汇编指令

  • mov:数据转移指令
  • push:数据入栈,同时esp栈顶寄存器也要发生改变
  • pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
  • sub:减法命令
  • add:加法命令
  • call:函数调用,1. 压入返回地址 2. 转入目标函数
  • jump:通过修改eip,转入目标函数,进行调用
  • ret:恢复返回地址,压入eip,类似pop eip命令

3.3解析函数栈帧的创建与销毁

3.3.1预备知识

首先我们达成一些预备知识才能有效的帮助我们理解,函数栈帧的创建和销毁。

  1. 每一次函数调用,都要为本次函数调用开辟空间,就是函数栈帧的空间。
  2. 这块空间的维护是使用了2个寄存器: esp 和 ebp , ebp 记录的是栈底的地址, esp 记录的是栈顶的地址。

如图所示:

函数栈帧的创建和销毁过程,在不同的编译器上实现的方法大同小异,本次演示以VS2022为例。

3.3.2 函数的调用堆栈

演示代码:

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);
	return 0;
}

这段代码我们调试以后查看调用堆栈,勾选【显示外部代码】

如下图:

我们可以看到mian函数在调用之前也是被调用的,main是由invoke_main函数来调用的,invoke_main前面的几个函数我们就不考虑了

每个栈帧建立的时候都有esp,ebp来维护栈帧空间

那就开始吧!

3.3.3 准备环境

为了让我们研究函数栈帧的过程足够清晰,不要太多干扰,我们可以关闭下面的选项,让汇编代码中排除一些编译器附加的代码:

3.3.4 转到反汇编

建议用32位的系统,这样汇编代码更多细节,调试到main函数开始执行的第一行,右击鼠标转到反汇编

这段是mian函数中所有的汇编代码

int main()
{
008B1840 55                   push        ebp  
008B1841 8B EC                mov         ebp,esp  
008B1843 81 EC E4 00 00 00    sub         esp,0E4h  
008B1849 53                   push        ebx  
008B184A 56                   push        esi  
008B184B 57                   push        edi  
008B184C 8D 7D DC             lea         edi,[ebp-24h]  
008B184F B9 09 00 00 00       mov         ecx,9  
008B1854 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
008B1859 F3 AB                rep stos    dword ptr es:[edi]  
	int a = 10;
008B185B C7 45 F8 0A 00 00 00 mov         dword ptr [ebp-8],0Ah  
	int b = 20;
008B1862 C7 45 EC 14 00 00 00 mov         dword ptr [ebp-14h],14h  
	int c = 0;
008B1869 C7 45 E0 00 00 00 00 mov         dword ptr [ebp-20h],0  

	c = Add(a,b);
008B1870 8B 45 EC             mov         eax,dword ptr [ebp-14h]  
008B1873 50                   push        eax  
008B1874 8B 4D F8             mov         ecx,dword ptr [ebp-8]  
008B1877 51                   push        ecx  
008B1878 E8 3C F8 FF FF       call        008B10B9  
008B187D 83 C4 08             add         esp,8  
008B1880 89 45 E0             mov         dword ptr [ebp-20h],eax  

}
int Add(int x, int y)
{
008B1780 55                   push        ebp  
008B1781 8B EC                mov         ebp,esp  
008B1783 81 EC CC 00 00 00    sub         esp,0CCh  
008B1789 53                   push        ebx  
008B178A 56                   push        esi  
008B178B 57                   push        edi  
	int z = 0;
008B178C C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0  
	z = x + y;
008B1793 8B 45 08             mov         eax,dword ptr [ebp+8]  
008B1796 03 45 0C             add         eax,dword ptr [ebp+0Ch]  
008B1799 89 45 F8             mov         dword ptr [ebp-8],eax  
	return z;
008B179C 8B 45 F8             mov         eax,dword ptr [ebp-8]  
}
008B179F 5F                   pop         edi  
008B17A0 5E                   pop         esi  
008B17A1 5B                   pop         ebx  
008B17A2 8B E5                mov         esp,ebp  
008B17A4 5D                   pop         ebp  
008B17A5 C3                   ret 

先看这一段,这里就是创建栈帧的过程

->1.

当这段汇编代码结束之后如图:

上面的这段代码最后4句,等价于下面的伪代码:

edi = ebp-0x24;
ecx = 9;
eax = 0xCCCCCCCC;
for(; ecx = 0; --ecx,edi+=4)
{
  *(int*)edi = eax;
}

我们还可以看内存中是怎么操作的,调试-窗口-内存

可以看到ebp上面的内容都被初始化为了cccccccc

小知识:烫烫烫~

之所以上面的程序输出“烫”这么一个奇怪的字,是因为main函数调用时,在栈区开辟的空间的其中每一个字节都被初始化为0xCC,而arr数组是一个未初始化的数组,恰好在这块空间上创建的,0xCCCC(两个连续排列的0xCC)的汉字编码就是“烫”,所以0xCCCC被当作文本就是“烫”。

->2.

->3.

->4.

销毁栈帧

->5.

3.3.5 整体结果图

标签:00,销毁,函数,int,mov,ebp,栈帧
From: https://blog.csdn.net/m0_73634434/article/details/141452613

相关文章

  • C/C++语言基础--字符串(包括字符串与字符数组、字符串与指针、字符串处理函数等),代码
    本专栏目的更新C/C++的基础语法,包括C++的一些新特性前言无论什么语言,字符串都是最重要、最基础的数据类型,他对二进制有很好的对应关系在C语言中没有提供专门的处理字符串的类型,但是我们可以通过字符数组、开辟内存地址来处理字符串本文将从字符串与字符数组的关系、字符......
  • 高数笔记 1.函数
    1.非正式定义我们在初中阶段就有接触过“函数”的概念,不妨回顾一下初二教科书的定义:一般地,在一个变化过程中,如果有两个变量\(x\)与\(y\),并且对于\(x\)的每一个确定的值,\(y\)都有唯一确定的值与之对应,那么我们说\(x\)是自变量,\(y\)是\(x\)的函数。如果当\(x=a\)时\(y=b\),那么\(b......
  • C++:虚函数和虚表详细总结
    一、虚函数(VirtualFunctions)1.定义虚函数是基类中使用virtual关键字声明的成员函数,支持动态多态性。通过基类指针或引用调用虚函数时,会根据实际对象类型选择调用相应的函数实现。2.声明和定义虚函数的声明:classBase{public:virtualvoidshow();//......
  • Clion配置-运行多个单独cpp代码的main函数
    修改CMakeLists.txt文件为project(YourProjectName)set(CMAKE_CXX_STANDARD11)#遍历项目二级目录下所有的.cpp文件file(GLOBfiles*/*.cpp)foreach(file${files})string(REGEXREPLACE".+/(.+)/(.+)\\..*""\\1-\\2"exe${file})add_exec......
  • 【Python】函数的定义和调用、形参和实参、函数的返回值、多元赋值、全局和局部变量
    文章目录函数的定义函数的调用形参和实参函数的返回值一个return多个return多元赋值变量作用域函数内的变量全局变量和局部变量修改全局变量函数的定义函数的定义:分配任务def函数名(形参列表): 函数体 return返回值def:define,定义形参列表中,可以有多个形......
  • Terraform - 初解Terraform - 函数
    Functions函数https://developer.hashicorp.com/terraform/language/functionsTerraform语言包括许多内置函数,可以从表达式中调用这些函数来转换。函数调用的一般语法是函数名后跟括号中以逗号分隔的参数:function(arg1,arg2)数值函数-max()获取最大值-min()获取最小......
  • 关于在得帆云数据中台如何自定义函数
    UDF使用示例场景说明:使用udf编写一个函数Unit_Conversion(value)。在函数中根据value的值进行单位转化,并进行类型转化。1、导入依赖在pom.xml中将如下依赖进行导入。<dependency><groupId>org.apache.hive</groupId><artifactId>hive-exec<......
  • C#使用委托实现函数回调,方法调用拦截
    C#使用委托实现函数回调,方法调用拦截回调方法、拦截方法定义publicclassAopHelper{publicstaticasyncTask<T>ExecuteGenericMethod<T>(Task<T>returnValue,Action<T>callBackAction,Action<Exception>exceptionAction,ActionfinallyAction){......
  • 一些关于生成函数的推导
    该文只推导一些特殊序列的生成函数1.$\quad$对于序列{\(a_n\)},\(a_n=1^n\),其生成函数为\(g(x)=\sum_{n=0}^{\infty}{a_nx^n}\)。$\quad$现在推导其封闭形式,先将其乘一个\(x\),可以得到:\[x\cdotg(x)=\sum_{n=0}^{\infty}{a_nx^{n+1}}\]$\quad$两式相减可得......
  • 生成函数(GF)
    学了一点皮毛,暂时先写一篇博客寄存一下定义:比较抽象的理解一下就是把一个限制条件的方案数转化成一个次冥函数的形式,再把一个次幂函数转化成某种限制条件下的方案数.......大概是这么一个形式:\[f(x)=a_{0}x^0+a_{1}x^1+a_{2}x^2+·····\]还是举个例子吧:你现在要离校回家......