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

敲代码的小王 敲代码的小王     2022-12-21     469

关键词:

栈帧的创建与销毁

什么是栈帧

C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。

首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。

注意:EBP指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;ESP所指的栈帧顶部和系统栈的顶部是同一个位置。

常用的寄存器有 eax,ebx,ecx,edx,esp,ebp;

上代码

接下来我们用一段简单的程序,来观察栈帧的创建与销毁。

#include<stdio.h>
int add(int x, int y)

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

int main()

	int a = 10;
	int b = 20;
	int c = 10;
	c = add(a, b);
	printf("%d", c);
	return 0;

首先,当我调试起来时,查看调用堆栈,当程序走到最后一行时,可以发现,main函数是被一个名为__tmainCRTStartup的函数调用的,而函数__tmainCRTStartup 又是被函数mainCRTStartup调用的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-

前面讲过ebp指向的是当前栈帧的底,esp指向当前栈帧的顶部,由于main是被函数__tmainCRTStartup调用的,所以在这里我们先画出__tmainCRTStartup函数的栈帧。

接下来,重新调试代码,转到反编译我们可以看到

00241410  push        ebp  
00241411  mov         ebp,esp  
00241413  sub         esp,0E4h  
00241419  push        ebx  
0024141A  push        esi  
0024141B  push        edi  
0024141C  lea         edi,[ebp-0E4h]  
00241422  mov         ecx,39h  
00241427  mov         eax,0CCCCCCCCh  
0024142C  rep stos    dword ptr es:[edi]

00241410  push        ebp  //将ebp的值压入栈中
00241411  mov         ebp,esp  //把esp的值赋值给ebp
00241413  sub         esp,0E4h  //将esp的值减去0E4h
//此时也就完成了对main函数空间的开辟(0E4h为开辟空间的大小)

空间开辟好了接下来就该对空间进行初始化了

00241419  push        ebx  //将ebx esi esi中的值压入栈中
0024141A  push        esi  
0024141B  push        edi  
0024141C  lea         edi,[ebp-0E4h]  //下面的意思是,将为main函数开辟的空间初始化为0CCCCCCCCh
00241422  mov         ecx,39h  
00241427  mov         eax,0CCCCCCCCh  
0024142C  rep stos    dword ptr es:[edi]

通过查看内存我们可以看到,此时已经初始化完成。

为main函数中的变量开辟空间

	int a = 10;
0024142E  mov         dword ptr [ebp-8],0Ah  //将0Ah(a--10)放到ebp-8的地址处
	int b = 20;
00241435  mov         dword ptr [ebp-14h],14h  //将14h(b--20)放到ebp-14h的地址处
	int c = 0;
0024143C  mov         dword ptr [ebp-20h],0    //将0(c--0)放到ebp-20的地址处

在内存中可以看到

	c = add(a, b);
00241443  mov         eax,dword ptr [ebp-14h]  //将ebp-14h(b)处的值放到eax寄存器中
00241446  push        eax  //将eax的值压入栈中
00241447  mov         ecx,dword ptr [ebp-8]  //将ebp-8(a)处的值放到寄存器ecx中
0024144A  push        ecx  //将ecx的值压入栈中
0024144B  call        002410E6//调用此地址处的函数,并将下一条指令的地址入栈00241450
00241450  add         esp,8  

通过这几行代码我们可以看到,在调用函数之前参数已经传了过去,在函数传参时,是从右向左开始传的,这里还充分证明了形参是实参的一份临时拷贝。此时我们看下现在的栈。

进入函数

0024144B  call        002410E6//调用函数

当调试箭头指向这一行时,我们可以按f11进入到函数中。

//和前面一样,为add函数开辟空间初始化
002413C0  push        ebp  //将ebp中的值压入栈中
002413C1  mov         ebp,esp  //将esp的值赋值给ebp
002413C3  sub         esp,0CCh  //将esp减去occh
002413C9  push        ebx  //ebx的值压入栈中
002413CA  push        esi  //esi的值压入栈中
002413CB  push        edi  //edi的值压入栈中
002413CC  lea         edi,[ebp-0CCh]  //初始化edp~(edp-occh)
002413D2  mov         ecx,33h  
002413D7  mov         eax,0CCCCCCCCh  
002413DC  rep stos    dword ptr es:[edi]

此时栈帧如下

进行函数运算

	int c = 0;
002413DE  mov         dword ptr [ebp-8],0  //将edp-8(c)出赋值为0
	c = x + y;
002413E5  mov         eax,dword ptr [ebp+8]  //将edp+8(x)处的值放到寄存器eax中
002413E8  add         eax,dword ptr [ebp+0Ch]  //将edp+0ch(y)处的值与寄存器eax中的值相加
002413EB  mov         dword ptr [ebp-8],eax  //将eax中的值赋值给ebp-8(c)
	return c;
002413EE  mov         eax,dword ptr [ebp-8]//将ebp-8位置处的数据放到寄存器eax中

栈帧的销毁

002413F1  pop         edi  //弹出栈顶放到寄存器edi
002413F2  pop         esi  //弹出栈顶放到寄存器esi
002413F3  pop         ebx  //弹出栈顶放到寄存器ebx
002413F4  mov         esp,ebp  //拷贝ebp的值到esp
002413F6  pop         ebp  //弹出栈顶放到寄存器ebp
002413F7  ret  //相当于pop,会出栈一次,程序将跳转到栈中地址的位置

函数add函数的栈帧被销毁,程序跳转到了函数调用前的下行处到此,函数栈帧的创建于销毁就结束了。

后面的调用printf函数可以参考上面add函数的过程,在这将不再描述。

00241450  add         esp,8  //寄存器esp+8
00241453  mov         dword ptr [ebp-20h],eax  //将eax中的值赋值到ebp-20h地址处(变量c的地址处)
	printf("%d", c);
00241456  mov         esi,esp  
00241458  mov         eax,dword ptr [ebp-20h]  
0024145B  push        eax  
0024145C  push        245858h  
00241461  call        dword ptr ds:[00249114h]  
00241467  add         esp,8  
0024146A  cmp         esi,esp  
0024146C  call        0024113B  
	return 0;
00241471  xor         eax,eax  


以上全部代码是在vs2013中进行的,不同的编译器还是有些差异,但是思想都是一致的,建议使用vc6++,或者使用vs2013级以前的版本进行。

8
0024146A cmp esi,esp
0024146C call 0024113B
return 0;
00241471 xor eax,eax


以上全部代码是在vs2013中进行的,不同的编译器还是有些差异,但是思想都是一致的,建议使用vc6++,或者使用vs2013级以前的版本进行。

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

函数栈帧🎂前言🌹栈帧的概念💖准备工作😀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这两个寄存器中... 查看详情