函数栈帧的创建与销毁(代码片段)

不倒翁* 不倒翁*     2023-03-22     790

关键词:

文章目录

1.函数栈帧的概念

函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的 一种数据结构。

对于栈,我们都知道栈是由高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,每个栈帧中维持着所需要的的各种信息。寄存器ebp指向当前栈帧的底部(高地址),寄存器esp指向当前栈帧的顶部(低地址)。

这样我们就了解了寄存器ebp和寄存器esp中存放的是地址,这两个地址是用来维护函数栈帧的。比如:调用main函数, 我们为main函数分配栈帧空间, 那么栈帧维护如下:

2.函数栈帧的创建

下面我们通过一段代码分析一下,函数栈帧创建和销毁的过程:(栈帧这部分内容在不同的编译器上实现存在差异, 但是思想大致都是一致的。本文是在vs2013编译器下实现的)

#include <stdio.h>

int Add(int x, int y)

	int z = 0;
	z = x + y;
	return z;


int main(void)

	int a = 10;
	int b = 20;
	int ret = 0;
	ret = Add(a, b);//计算a+b
	printf("%d\\n", ret);
	return 0;

我们打开调试,选中调用堆栈窗口可以看到,main函数也是被其他函数调用的。

可以看出,main函数是在__tmainCRTStartup函数内部被调用的,而__tmainCRTStartup函数又是在mainCRTStartup函数内部调用的。
为了能更加清楚的看到栈帧创建和销毁的过程,我们转到上面代码对应的反汇编代码:

![int main(void)

009D3F40  push        ebp  //将edp压入栈帧
009D3F41  mov         ebp,esp  //将esp的值赋给edp
009D3F43  sub         esp,0E4h  //esp-0E4h
009D3F49  push        ebx  
009D3F4A  push        esi  
009D3F4B  push        edi  
009D3F4C  lea         edi,\\[ebp+FFFFFF1Ch\\]  
009D3F52  mov         ecx,39h  
009D3F57  mov         eax,0CCCCCCCCh  
009D3F5C  rep stos    dword ptr es:\\[edi\\]]
	int a = 10;
009D3F5E  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
009D3F65  mov         dword ptr [ebp-14h],14h  
	int ret = 0;
009D3F6C  mov         dword ptr [ebp-20h],0  

	ret = Add(a, b);//计算a+b
009D3F73  mov         eax,dword ptr [ebp-14h]  
009D3F76  push        eax  
009D3F77  mov         ecx,dword ptr [ebp-8]  
009D3F7A  push        ecx  
009D3F7B  call        009D11F9  
009D3F80  add         esp,8  
009D3F83  mov         dword ptr [ebp-20h],eax  

	printf("%d\\n", ret);
009D3F86  mov         esi,esp  
009D3F88  mov         eax,dword ptr [ebp-20h]  
009D3F8B  push        eax  
009D3F8C  push        9D5860h  
009D3F91  call        dword ptr ds:[009D9118h]  
009D3F97  add         esp,8  
009D3F9A  cmp         esi,esp  
009D3F9C  call        009D1140  
	return 0;
009D3FA1  xor         eax,eax  

009D3FA3  pop         edi  
009D3FA4  pop         esi  
009D3FA5  pop         ebx  
009D3FA6  add         esp,0E4h  
009D3FAC  cmp         ebp,esp  
009D3FAE  call        009D1140  
009D3FB3  mov         esp,ebp  
009D3FB5  pop         ebp  
009D3FB6  ret  

2.1 main函数函数栈帧的创建过程

009D3F40  push        ebp  //将edp压入栈帧
009D3F41  mov         ebp,esp  //将esp的值赋给edp
009D3F43  sub         esp,0E4h  //esp-0E4h 将esp向上(低地址方向)移动4个字节
009D3F49  push        ebx  //接下来三行是将 ebx esi edi 压入栈顶
009D3F4A  push        esi  
009D3F4B  push        edi  
009D3F4C  lea         edi,\\[ebp+FFFFFF1Ch\\]   //然后将main函数的函数栈帧初始化为0cccccccch
009D3F52  mov         ecx,39h  
009D3F57  mov         eax,0CCCCCCCCh  
009D3F5C  rep stos    dword ptr es:\\[edi\\]]

第一步是进行push命令,将ebp压入栈顶 将ebp压入栈顶后,esp的地址也会随之改变
第二步进行move指令,将esp的值给ebp
第三步进行sub指令,将esp指向的地址减去0E4h(十进制228)
随后分别把ebx,esi,edi压入栈顶
随后四句指令,把从edi(ebp-0E4h)开始的ecx(39h)个 空间改成eax(0CCCCCCCCh)。这么做是为了给main函数栈帧初始化

2.2 main函数中创建变量

	int a = 10;
009D3F5E  mov         dword ptr [ebp-8],0Ah  //把0Ah赋值给内存地址为ebp-8中的双字节的空间
	int b = 20;
009D3F65  mov         dword ptr [ebp-14h],14h  //把14h赋值给内存地址为ebp-14h中的双字节的空间
	int ret = 0;
009D3F6C  mov         dword ptr [ebp-20h],0  //把0赋值给内存地址为ebp-20h中的双字节的空间

2.3 Add函数函数栈帧的创建

当abc变量创建好了之后,就要开始调用add函数。
随后分别将ebx(20)和ecx(10)压入栈顶。
这两个指令实际上是在为Add函数传参

	ret = Add(a, b);//计算a+b
009D3F73  mov         eax,dword ptr [ebp-14h]  
009D3F76  push        eax  
009D3F77  mov         ecx,dword ptr [ebp-8]  
009D3F7A  push        ecx  
009D3F7B  call        009D11F9  
009D3F80  add         esp,8  


随后call指令是调用函数指令,它会把call指令的下一条指令的地址压入栈顶。这样做的目的是等Add函数调用结束后,就会回到call指令的下一条指令继续执行。

进入Add函数后,前面的几条指令跟进入main之前的几条指令一样,是为了给函数准备栈帧和对其进行初始化。

随后在ebp-8的空间创建临时变量z并初始化为0,再通过ebp+8和
ebp+0Ch找到main函数中传递的a,b参数作为形参x,y,相加得到的值赋给eax,再由eax把值赋给z。

2.4 Add函数栈帧的销毁



随后ret指令找到之前已经压入栈的call指令的下一条指令的地址 ,就回到主函数的call指令的下一条指令。
再将esp指向的地址加8,把形参的内存释放。
在通过ebp-20h找到main函数的c变量,然后再把eax的值赋给c,实现了把Add函数计算结果带回到main。

图解函数栈帧-函数的创建与销毁(代码片段)

函数栈帧🎂前言🌹栈帧的概念💖准备工作😀main函数栈帧的创建及初始化😁main函数的被调用😂main函数栈帧的开辟🤣main函数栈帧的初始化👩临时变量的创建。👨Add函数栈帧的创建🧑Add... 查看详情

函数栈帧的创建与销毁(代码片段)

...什么是栈帧C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈... 查看详情

函数栈帧的创建与销毁(代码片段)

文章目录1.函数栈帧的概念2.函数栈帧的创建2.1main函数函数栈帧的创建过程2.2main函数中创建变量2.3Add函数函数栈帧的创建2.4Add函数栈帧的销毁1.函数栈帧的概念函数栈帧:使用每一个函数都要在栈区开辟一块空间.栈帧也叫过程活... 查看详情

函数栈帧的创建与销毁,带你了解代码底层原理(代码片段)

...么创建的?(2)为什么局部变量的值是随机的?(3)函数是怎么传参的?传参的顺序如何?(4)形参和实参是什么关系?(5)函数调用是怎么做的?(6)函数调用结束后是怎么返回的?这些疑问其实都和函数栈帧... 查看详情

函数栈帧的创建和销毁(代码片段)

...的说明二、对于创建和销毁的全过程1.对于_mainCRTstarup的函数的创建2.对于main函数的创建(1).为什么有时候会打印出烫烫烫3.对于Add的函数的创建(2).为什么说形参不在函数中(3).函数中return值如何放回的>(4).ebp-main出栈后ebp寄存器... 查看详情

c语言进阶顶级神功!函数栈帧的创建和销毁(代码片段)

...垫正文开始1.大致轮廓了解(源代码及反汇编)2.函数栈帧的创建和销毁总流程3.函数栈帧的创建3.1main函数的创建(分解)3.2Add函数的创建(分解)4.函数栈帧的销毁4.1main函数的销毁(分解)5.1Add函... 查看详情

函数栈帧的创建和销毁(代码片段)

目录各种寄存器的作用main()函数的调用通过汇编观察函数调用过程main()函数栈帧开辟过程Add()函数栈帧开辟过程Add()函数栈帧销毁过程各种寄存器的作用eax是“累加器”(accumulator),它是很多加法乘法指令的缺省寄存器ebx是“... 查看详情

函数栈帧的创建和销毁(待写)(代码片段)

函数栈帧的创建和销毁main函数被调用的过程:具体过程main函数被调用的过程:mainCRTStartup()调用_tmainCRTStartup()再调用main()寄存器:ebp(栈底指针),esp(栈顶指针)(sp是esp的低16位,esp是rsp的低32位,ss是16位堆栈... 查看详情

学好c语言,还需要掌握这个内功——函数栈帧的创建与销毁(代码片段)

...是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参的?传参的顺序是什么?形参和实参是什么关系?函数调用结束后怎么返回?看完这篇文章,一切将豁然开朗……预备知识在进入正题之... 查看详情

图解c/c++语言底层:函数调用过程之函数栈帧的创建和销毁(上)(代码片段)

**文章目录函数栈帧的创建和销毁什么是寄存器?寄存器分类寄存器用途什么是"栈"?函数栈帧的概念函数压栈的过程示例代码和主函数汇编指令(部分)汇编指令:构建函数栈帧准备(一)汇编指令:构建函数栈... 查看详情

c语言深入逐汇编详解函数栈帧的创建和销毁过程(代码片段)

【C语言深入】逐汇编详解函数栈帧的创建和销毁过程一、图解大概过程二、函数栈帧的创建过程1、简介一些需要用到的汇编指令和寄存器2、调用main函数的函数3、局部变量的初始化4、形成临时拷贝5、函数调用6、形成栈帧7、提... 查看详情

c语言学习--函数栈帧的创建和销毁(代码片段)

...说局部变量未初始化时,其中存储的时随机值?函数到底时如何传参的?实参传递的顺序又是怎样的?形参和实参之间有着什么关系?函数调用结束后,结果是如何返回的?这些问题大 查看详情

函数栈帧的创建和销毁——“c”(代码片段)

...你们好呀,今天小雅兰来为大家介绍一个知识点——函数栈帧的创建和销毁。其实这个知识点,我们很早之前就要讲,但是因为我的一系列原因,才一直拖到了现在,那么,话不多说,让我们一起进入... 查看详情

内功修炼《函数栈帧的创建和销毁》建议收藏(代码片段)

...️⃣为什么未初始化的局部变量的值是随机值?3️⃣函数是如何传参的?以及传参的顺序是怎样的?4️⃣形参和实参是什么关系?5️⃣ 查看详情

函数的调用过程,栈帧的创建和销毁。

一.函数调用1.函数调用过程涉及到的寄存器: (1)esp:栈指针寄存器(extendedstackpointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 (2)ebp:基址指针寄存器(extendedbasepointer),其内存放着一个... 查看详情

图解c/c++底层:函数栈帧的创建和销毁(下篇)

函数栈帧的创建和销毁(下篇)上篇原文链接根据上篇的函数栈帧过程的学习,我们了解到:什么是寄存器?计算机的速度最快的存储单元,因为寄存器是集成在CPU之上的,与内存是不同的独立的存储空间。什么... 查看详情

c语言函数栈帧的创建和销毁,以简单函数的调用来进行详细刨析(代码片段)

1、为什么需要了解函数的栈帧大家对于程序很少有人会对底层的东西刨根问底,很多人写程序大多就是写出来能够成功运行起来就大无所措了,但是很少有人对程序是怎么跑起来较为关心,比如:局部变量是怎么... 查看详情

理解函数栈帧

函数栈帧的创建与销毁一、函数栈帧的创建1.寄存器2.函数栈帧3.函数中调用函数二、函数栈帧的销毁总结一、函数栈帧的创建1.寄存器一般来说,计算机中的寄存器有六种分别是:eax,ebx,ecx,edx,ebp,esp而ebp,esp这两个寄存器中... 查看详情