首页 > 其他分享 >超详细C语言函数栈帧的创建和销毁的过程讲解,细节满满

超详细C语言函数栈帧的创建和销毁的过程讲解,细节满满

时间:2024-11-09 23:45:15浏览次数:7  
标签:满满 函数 esp C语言 地址 ebp main 栈帧 变量

目录

一、ebp和esp

二、main函数的函数栈帧(解释反汇编)

(1)、push操作

(2)、mov操作

(3)、sub操作

(4)、然后又是三个push操作

(5)、lea操作:

(6)、接着四步操作:

三、如何创建的变量(创建变量的反汇编)

(1)、第一个int a = 10:

此时如果没有给a变量赋值,那么该位置的值就是CCCCCCCC,这就是为什么未初始化的局部变量默认为随机值的原因。

(2)、变量b、c创建的过程同理:

四、函数调用、传参过程

(1)、对变量b操作

(2)、对变量a操作

执行call指令后,call指令还会将它下一条指令的地址压栈:原因是因为,当我们把调用的函数执行完后,我们需要回到main函数,就会通过这个地址回来。

五、被调用函数的栈帧

(1)、变量的相加

此时我们就可以理解:形参是实参的临时拷贝

六、函数返回值的过程

此时我们就可以理解为什么函数调用结束后,会把局部变量给销毁,但也可以把值传出来,因为值是放在了eax寄存器里面保存起来了。

(2)、pop:弹出出栈 

(3)、回收函数空间:

此时执行这条指令就会返回到刚开始call指令的下一条指令 :


(1)、首先要注意函数栈帧在不同的编译器下面都有所差异

(2)、首先我们需要认识几个寄存器eax、ebx、ecx、edx、ebp、esp。这些会在后续讲解中出现,其中最重要的就是ebp和esp。

(2)、ebp、esp里面存放的是地址,esp存放低地址,ebp存放高地址,,这两个地址是用来维护函数栈帧的。

一、ebp和esp

下面用一段简单的代码来介绍:

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;
}

注意每个函数调用,都要在栈区创建一个空间:

当前我们在调用main函数,所以在栈区开辟了一块空间,esp就指向这块空间的低地址,ebp指向这块空间的高地址用于维护这块空间,在调用哪个函数,这两个寄存器就去维护哪块空间。又因为栈区的空间是从高地址向低地址使用的,所以esp被成为栈顶指针,ebp被称为栈低指针。

二、main函数的函数栈帧(解释反汇编)

首先我们看到要知道在vs2013中main函数还会被其他函数调用:

然后我们来看main函数的反汇编:

刚开始还在是调用main函数那个函数的栈帧:

(1)、push操作

即压栈,意思是把ebp压入栈顶。

(2)、mov操作

可以理解成赋值,即将esp赋值给ebp指针,这样ebp也指向esp栈顶

(3)、sub操作

即减操作,意思是将esp减去0E4h(这是十六进制数字)地址,因为栈区是高地址向低地址使用,所以esp会往上走指向某个地址。

此时紫色这块空间其实就是为main函数申请的空间(main的栈帧)

(4)、然后又是三个push操作

注意push有个动作,push后esp指针会跟着动,所以push完后如下:

(5)、lea操作:

加载有效地址,即将后面那个地址放进edi里面去

(6)、接着四步操作:

更改相应指针的值后,将edi指向的地址向下39h个位置,每dword为一个单位(dword叫双字,即四个字节)的值全部改为0CCCCCCCCh的值。(刚好等于刚刚给main函数开辟的栈帧空间)

此时给main函数开辟栈帧这个准备就准备完了。

三、如何创建的变量(创建变量的反汇编)

main函数栈帧准备完后就开始创建变量了:

(1)、第一个int a = 10:

是将0Ah(十进制就是数字10),mov(移动或赋值)到[ebp-8]的位置,即main函数栈帧中的一个位置:

此时如果没有给a变量赋值,那么该位置的值就是CCCCCCCC,这就是为什么未初始化的局部变量默认为随机值的原因。

(2)、变量b、c创建的过程同理:

四、函数调用、传参过程

(1)、对变量b操作

首先把ebp-14h里面的值放进eax,即把变量b的值20放进eax,然后将eax压栈,即将20压栈,因为push操作,注意esp也会做出相应移动:

(2)、对变量a操作

这两条指令与上面同理:

(3)、call操作,即调用,此时按快捷键f11进入

执行call指令后,call指令还会将它下一条指令的地址压栈:原因是因为,当我们把调用的函数执行完后,我们需要回到main函数,就会通过这个地址回来。

接着再按下f11就会进入被调用函数的栈帧

五、被调用函数的栈帧

通过上述步骤后就来到了被调用函数的栈帧:

我们会发现前面准备工作的几条指令和main函数栈帧都差不多,在为该函数分配内存开辟空间:

(1)、变量的相加

因为前面操作都Main一样,所以直接跳到变量相加的地方,此时形参x,y就出现了

结合上面我们知道ebp+8就是变量a传参的位置:

同理ebp+0ch(ebp+12)就是变量b传参的位置:

然后将相加的值(add操作)放到ebp-8的位置,即赋值给变量z。

此时我们就可以理解:形参是实参的临时拷贝

六、函数返回值的过程

 (1)、第一个操作,把ebp-8(即z的值(返回值))转移到eax寄存器。

此时我们就可以理解为什么函数调用结束后,会把局部变量给销毁,但也可以把值传出来,因为值是放在了eax寄存器里面保存起来了。

(2)、pop:弹出出栈 

 出栈后,esp指针会跟着向下移动,此时把三个寄存器给出栈了

(3)、回收函数空间:

到这就开始回收空间了,把ebp的值给esp:

根据上述,我们知道ebp在Add函数栈帧的下面:

然后又执行pop指令:

把ebp给pop了,而这个位置我们放的是main的ebp地址,所以把这个ebp弹出后,ebp指针就会找到main函数的ebp地址去,然后有pop操作,esp也会移动。

此时就彻底回到main函数的栈帧了

(4)、ret操作:

此时执行这条指令就会返回到刚开始call指令的下一条指令 :

在继续执行add指令,此时形参就销毁了

标签:满满,函数,esp,C语言,地址,ebp,main,栈帧,变量
From: https://blog.csdn.net/hffh123/article/details/143641196

相关文章

  • 华为OD机试2024年E卷-MVP争夺战[100分]( Java | Python3 | C++ | C语言 | JsNode | Go
    题目描述在星球争霸篮球赛对抗赛中,最大的宇宙战队希望每个人都能拿到MVP,MVP的条件是单场最高分得分获得者。可以并列所以宇宙战队决定在比赛中尽可能让更多队员上场,并且让所有得分的选手得分都相同,然而比赛过程中的每1分钟的得分都只能由某一个人包揽。输入描述输入第一行......
  • 华为OD机试2024年E卷-AI识别面板[100分]( Java | Python3 | C++ | C语言 | JsNode | Go
    题目描述AI识别到面板上有N(1≤N≤100)个指示灯,灯大小一样,任意两个之间无重叠。由于AI识别误差,每次别到的指示灯位置可能有差异,以4个坐标值描述AI识别的指示灯的大小和位置(左上角x1,y1,右下角x2,y2),请输出先行后列排序的指示灯的编号,排序规则:每次在尚未排序的灯中挑选最高的......
  • 函数的栈帧空间创建与销毁全过程(详解~)
    目录一.什么是函数栈帧?二.理解函数栈帧的创建能解决哪些问题?三.创建函数栈帧空间的之前认知3.1什么是栈3.2认识相关寄存器3.3汇编指令四.创建和销毁全过程4.1预备知识4.1.1调用堆栈4.2打开反汇编4.3函数栈帧创建​编辑4.4函数栈帧销毁一.什么是函数栈......
  • c语言中没有返回值的函数和不含形参的函数
     001、没有返回值的函数[root@PC1test]#lstest.c[root@PC1test]#cattest.c##测试c程序#include<stdio.h>voidput_star(inta)//定义不含返回值的函数{while(a-->0)putchar('*');//函数中没有ret......
  • C++入门(C语言语法改进篇)
    目录C++第一个程序命名空间namespace的价值namespace定义命名空间的使用C++输入输出缺省参数全缺省参数半缺省参数函数重载参数类型不同参数个数不同C++第一个程序C++的文件名称后缀为.cpp,C++包含了C语言的大部分语法,所以在.cpp文件里面我们依然可以使用C语言编程......
  • C语言数据结构之二叉树(BINARY TREE)的多种数据类型存贮
    C语言数据结构之二叉树(BINARYTREE)的多种数据类型存贮用无类型指针(void*)来做为基本数据类型来存贮数据,将其他数据类型强制转化为无类型指针,从而达到目标!!!输出函数指针BTFunc比较函数指针BTCmpFunc返回值为整型值1、-1、0,表示大于、小于、相等代码如下:/*filename:btr......
  • C语言期末必练题目——part 5
    9. #include“stdio.h” func(int b[]) { int j;    for(j=0;j<4;j++)      b[j]=j; } main() { int a[4],i;    func(a);    for(i=0;i<4;i++)       printf(“%2d”,a[i]); }运行结果为:     ......
  • Air780E软件指南:C语言内存数组(zbuff)
    一、ZBUFF(C内存数组)简介zbuff库可以用c风格直接操作(下标从0开始),例如buff[0]=buff[3]可以在sram上或者psram上申请空间,也可以自动申请(如存在psram则在psram进行申请,如不存在或失败则在sram进行申请)。操作里面的元素时,可以根据光标进行增删改查。偏移方式有三种:从头......
  • c语言中返回整数值的长度
     001、方法1while循环[root@PC1test]#lstest.c[root@PC1test]#cattest.c##测试c程序#include<stdio.h>intget_length(inta){intlength=0;while(a>0){length++;a/=10;......
  • c语言--数组
    目录1数组创建 1.1定义定长数组1.2定义并初始化数组1.3定义部分初始化的数组2.动态数组(动态分配)2.1使用malloc动态创建数组2.2使用calloc动态创建数组2.3动态数组初始化2.4释放动态数组内存3.变长数组(VLA,VariableLengthArray)4.字符串和字符数组4.1.......