函数栈帧的创建和销毁

1.什么是函数栈帧?

我们在写C语言代码的时候,经常会把一个独立的功能抽象为函数,所以C程序是以函数为基本单位的。那函数是如何调用的?函数的返回值又是如何待会的?函数参数是如何传递的?这些问题都和函数栈帧有关系。
函数栈帧(stack frame)就是函数调用过程中在程序的调用栈(call stack)所开辟的空间
这些空间是用来存放:
函数参数和函数返回值
临时变量(包括函数的非静态的局部变量以及编译器自动生产的其他临时变量)
保存上下文信息(包括在函数调用前后需要保持不变的寄存器)。

2.理解函数栈帧能解决什么问题?

理解函数栈帧有什么用呢?
只要理解了函数栈帧的创建和销毁,以下问题就能够很好的理解了:
局部变量是如何创建的?
为什么局部变量不初始化内容是随机的?
函数调用时参数时如何传递的?传参的顺序是怎样的?
函数的形参和实参分别是怎样实例化的?
函数的返回值是如何带回的?

2.什么是栈?

栈很重要,每一个程序都需要用到栈,没有栈的存在,就没有局部变量,函数的存在,没有我们如今看到的计算机语言的存在
栈就相当于一种容器,用户可以将数据存入里面(入栈),也可以将存入栈中的数据弹出(出栈),但是需要遵守一条规则,先入后出,就是先入栈的数据要后出栈,像很多书堆在一起,先放的书在最下面,最后才能取出来
入栈使得栈增大,出栈使得栈减小,在经典的操作系统中,栈总是由下增长的(就是从高到低的来)

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

相关寄存器
eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
ebp:栈底寄存器
esp:栈顶寄存器
eip:指令寄存器,保存当前指令的下一条指令的地址
相关汇编命令
mov:数据转移指令
push:数据入栈,同时esp栈顶寄存器也要发生改变
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
sub:减法命令
add:加法命令
call:函数调用,1. 压入返回地址 2. 转入目标函数
jump:通过修改eip,转入目标函数,进行调用
ret:恢复返回地址,压入eip,类似pop eip命令

4.正式解析函数栈帧的创建和销毁

预备知识:
在寄存器中有 eax,ebx,ecx,edx, 而想要了解函数栈帧,需要知道ebp,esp这两个寄存器中存放的是地址,而这两个地址是用来维护函数栈帧的,每一个函数调用,都要在栈区创建一个空间
如:add函数的函数栈帧的实现:
在这里插入图片描述
main函数也是被其他函数调用的:__tmainCRTStartup ,而这个函数又是被mainCRTStartup调用的
在这里插入图片描述
在这里插入图片描述
把main函数来转汇编语言来更好的分析:

int main()
{
00AD1410  push        ebp  
00AD1411  mov         ebp,esp  
00AD1413  sub         esp,0E4h  
00AD1419  push        ebx  
00AD141A  push        esi  
00AD141B  push        edi  
00AD141C  lea         edi,[ebp+FFFFFF1Ch]  
00AD1422  mov         ecx,39h  
00AD1427  mov         eax,0CCCCCCCCh  
00AD142C  rep stos    dword ptr es:[edi]  
	int a = 10;
00AD142E  mov         dword ptr [ebp-8],0Ah  
	int b = 20;
00AD1435  mov         dword ptr [ebp-14h],14h  
	int c = 0;
00AD143C  mov         dword ptr [ebp-20h],0  
	c = add(a, b);
00AD1443  mov         eax,dword ptr [ebp-14h]  
00AD1446  push        eax  
00AD1447  mov         ecx,dword ptr [ebp-8]  
00AD144A  push        ecx  
00AD144B  call        00AD10E6  
00AD1450  add         esp,8  
00AD1453  mov         dword ptr [ebp-20h],eax  
	printf("%d\n", c);
00AD1456  mov         esi,esp  
	printf("%d\n", c);
00AD1458  mov         eax,dword ptr [ebp-20h]  
00AD145B  push        eax  
00AD145C  push        0AD5858h  
00AD1461  call        dword ptr ds:[00AD9114h]  
00AD1467  add         esp,8  
00AD146A  cmp         esi,esp  
00AD146C  call        00AD113B  
	return 0;
00AD1471  xor         eax,eax  
}

5.关于add函数反汇编的解析

1: 先从main函数开始开辟空间

int main()
{
//先是准备工作:为main函数创建空间
00AD1410  push        ebp  
//把ebp中的值进行压栈,存放进去放入刚开的invoke_main中的ebp
00AD1411  mov         ebp,esp  
//开始移动,把esp中的值放入ebp中,为main函数开辟了ebp
00AD1413  sub         esp,0E4h 
//给esp减去一个0E4h 
00AD1419  push        ebx  
//压栈入一个ebx
00AD141A  push        esi  
//压栈入一个esi
00AD141B  push        edi 
//压栈入一个edi 
00AD141C  lea         edi,[ebp+FFFFFF1Ch] 
//lea为加载 ,把[ebp+FFFFFF1Ch]加入到edi中
00AD1422  mov         ecx,39h  
//把39h放到ecx中
00AD1427  mov         eax,0CCCCCCCCh 
//把 0CCCCCCCCh放入到eax中
00AD142C  rep stos    dword ptr es:[edi] 
//从edi开始向下39h次的四个字节的数据,空间都改成eax的内容,改成0CCCCCCCCh 
//开始a,b,c的创建,代表着正式开始
	int a = 10;
00AD142E  mov         dword ptr [ebp-8],0Ah  
//把0Ah的值放入ebp-8的地方  就是把10放进去
	int b = 20;
00AD1435  mov         dword ptr [ebp-14h],14h  
//把14h的值放入到ebp-14h的地方,就是把20放进去
	int c = 0;
00AD143C  mov         dword ptr [ebp-20h],0  
//把0的值放入到ebp-20h的地方,就是把0放进去
//开始add函数的创建
	c = add(a, b);
00AD1443  mov         eax,dword ptr [ebp-14h]  
//把ebp-14h放入到eax中,就是把b的20放入eax中
00AD1446  push        eax 
//开始压栈 
00AD1447  mov         ecx,dword ptr [ebp-8]  
//把ebp-8放入到ecx中,就是把a的10放入到ecx中
00AD144A  push        ecx  
//开始压栈
00AD144B  call        00AD10E6  
//开始调用

2:开始为add函数开辟空间

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int add(int x, int y)
{
00AD13C0  push        ebp  
//压栈
00AD13C1  mov         ebp,esp  
//把esp给了ebp
00AD13C3  sub         esp,0CCh  
//给esp减上0CCh
00AD13C9  push        ebx  
00AD13CA  push        esi  
00AD13CB  push        edi  
//压栈 放入三个元素
00AD13CC  lea         edi,[ebp+FFFFFF34h]  
00AD13D2  mov         ecx,33h  
00AD13D7  mov         eax,0CCCCCCCCh  
00AD13DC  rep stos    dword ptr es:[edi]  
//把[ebp+FFFFFF34h]加载到edi里,往下33h次的空间放入0CCCCCCCh
	int z = 0;
00AD13DE  mov         dword ptr [ebp-8],0  
//把0放到ebp-8的这里
	z = x + y;
00AD13E5  mov         eax,dword ptr [ebp+8]  
//把ebp+8放入到eax中,就是10放入到eax
00AD13E8  add         eax,dword ptr [ebp+0Ch] 
// 把ebp+0ch放入到eax中,把10加上20放到eax中
00AD13EB  mov         dword ptr [ebp-8],eax  
//把eax中的30放入ebp-8的地方
	return z;
00AD13EE  mov         eax,dword ptr [ebp-8] 
//把ebp-8的值30放入eax寄存器当中 
}
00AD13F1  pop         edi  
00AD13F2  pop         esi  
00AD13F3  pop         ebx  
//一个一个的释放弹出  
00AD13F4  mov         esp,ebp  
//把ebp赋值给esp
00AD13F6  pop         ebp  
//最后把ebp给弹出
00AD13F7  ret 

3:最后回到add函数中,将寄存器里的值给传回来的过程

00AD1450  add         esp,8  
//esp+8指向x,y,把xy的值10,20给释放
00AD1453  mov         dword ptr [ebp-20h],eax 
// 把eax的值放入到ebp-20h,也就是c当中
	printf("%d\n", c);
00AD1456  mov         esi,esp  
//把esp赋值给esi中
	printf("%d\n", c);
00AD1458  mov         eax,dword ptr [ebp-20h]  
//把ebp-20h,c的值30移到eax中
00AD145B  push        eax  
00AD145C  push        0AD5858h  
//弹出 释放
00AD1461  call        dword ptr ds:[00AD9114h]  
00AD1467  add         esp,8  
00AD146A  cmp         esi,esp  
00AD146C  call        00AD113B  
	return 0;
00AD1471  xor         eax,eax  
}

6.函数栈帧的创建过程(画图详解)

在这里插入图片描述

7.函数栈帧的销毁(画图详解)

在这里插入图片描述

总结

感谢观看,欢迎三连,如有错误,欢迎指正。
此篇文章为转载
原文链接:https://blog.csdn.net/weixin_45659943/article/details/124447995

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值