C语言图解栈帧的形成与释放【全网第二详细分析版】

前言

🎉欢迎关注🔎点赞👍收藏⭐️留言📝
🎉推荐up主专题文章【C语言编程一百题
📌QQ:3052645092 有问题可以一起讨论哦
🔎超级好用的刷题网站【牛客网

在这里插入图片描述
在这里插入图片描述

🍭作者水平很有限,如果发现错误,请及时告知作者哦!感谢感谢!

栈帧

#include<stdio.h>
int MyAdd(int a, int b){
	int c = 0;
	c = a + b;
	return c;
}
int main(){
	int x = 0xA;
	int y = 0xB;
	int z = 0;
	z = MyAdd(x, y);
	printf("%d\n", z);
	return 0;
}

我们用这个代码做测试

相关的寄存器

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

相关指令

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

开始!!!

main函数调用MyAdd函数如何形成栈帧的我们就是讲解这个,这个明白啦,函数调用形成栈帧就明白啦
在这里插入图片描述

反汇编
在这里插入图片描述

主函数也是函数也会形成栈帧
在这里插入图片描述
在这里插入图片描述

x的初始化

在这里插入图片描述
在这里插入图片描述

y的初始化

在这里插入图片描述在这里插入图片描述

z初始化

在这里插入图片描述

在这里插入图片描述

形成临时拷贝

在这里插入图片描述

y的值被压入栈

在这里插入图片描述

在这里插入图片描述

x的值被压入栈

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

总结:1,临时变量在函数被调用之前就已经形成了,临时拷贝 2,形参实例化的顺序是从右向左的!!!

在这里插入图片描述

接下来我们要调用函数了!!

在这里插入图片描述

函数被调用,其返回地址要被压栈保护

在这里插入图片描述
执行之前
在这里插入图片描述
执行之后
在这里插入图片描述

jump之前, jump修改eip的值,进入目标函数!!!

在这里插入图片描述
jump之后
在这里插入图片描述进入MyAdd函数内部
在这里插入图片描述

现在我们正式进入了MyAdd函数

在这里插入图片描述

形成MyAdd函数栈帧指令1

在这里插入图片描述

ebp寄存器的内容压入栈!!!

在这里插入图片描述

未入栈之前

在这里插入图片描述

入栈之后

在这里插入图片描述

入栈成功

在这里插入图片描述

指令2

在这里插入图片描述
在这里插入图片描述

完成

在这里插入图片描述

执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

到这可能有疑问,哪ebp之前存的是main函数栈底地址,那么esp的内容又把ebp的内容覆盖了,哪还能找回来吗?
答案是不用担心,我们已经push入栈了!!!

指令3形成栈帧的指令

在这里插入图片描述

形成前

在这里插入图片描述

形成后

在这里插入图片描述

现在我们的My Add函数的栈帧形成了 以下的一些代码我们关心,是一些对内存初始化的

在这里插入图片描述

我们直接来看MyAdd函数里面的临时变量的形成

在这里插入图片描述

此指令就是,把ebp-8所指向的这个内存单元赋上0

在这里插入图片描述

c=a+b的执行

在这里插入图片描述

以上三条指令的执行

在这里插入图片描述
在这里插入图片描述

汇编层面

第一条指令执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

第二条执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

第三条执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

**c=a+b;执行完成

栈帧的形成我们已经完成了,还有一个细节要提一下

在这里插入图片描述

形成栈帧就是esp-某个值之后形成新的栈顶,和栈底地址形成了一块内存空间,但是问题是,编译器为啥能知道,要减多少呢,要形成多大的内存空间的!!!
解释:编译器是有能力知道的,总所周知,C语言是有类型关键字的,比如:int double float等,还有一个关键字叫左sizeof可以求各个类型数据的大小,那么sizeof求大小,是在编译时求还是在运行之后求出来呢?
答案是在编译时,因为sizeof是关键字。不是函数!!!因为这一点,编译器就有能力,判断出栈帧该申请多大空间

返回

在这里插入图片描述

本代码意思就是:把ebp-8内存空间的值,即c的值放到寄存器eax中 函数的返回值是通过寄存器的方式返回的

在这里插入图片描述

这三条我们不关心 主要看着3条

在这里插入图片描述

第一条指令

在这里插入图片描述

执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

第二条指令

在这里插入图片描述

pop是弹栈指令,把栈顶的内容弹入ebp寄存器

在这里插入图片描述

执行之前

在这里插入图片描述

执行之后

在这里插入图片描述

第三条指令

在这里插入图片描述

ret返回地址压入eip,类似于pop eip

在这里插入图片描述

在这里插入图片描述

执行ret之后

在这里插入图片描述

安全返回,现在我们已经返回到了main函数

看这一条指令,x和y的释放

在这里插入图片描述

add esp, 8 意思是esp+8

在这里插入图片描述

最后一步啦

在这里插入图片描述

最后把eax寄存器放到z的空间

在这里插入图片描述

至此栈帧的调用和释放及返回操作完毕

根据临时拷贝的地址改变另一个临时拷贝

#include<stdio.h>
int MyAdd(int a, int b){
	printf("before: %d\n",b);
	*(&a + 1) = 100;//整型指针+1,本质是加上其所指向类型的大小
	printf("after: %d\n", b);
	return 0;
}
int main(){
	int x = 0xA;
	int y = 0xB;
	int z = 0;
	z = MyAdd(x, y);
	printf("%d\n", z);
	return 0;
}

通过形参a的地址来改形参b的值

在这里插入图片描述

总结

  1. 调用函数,需要先形成临时拷贝,形成过程是从右向左的
    在这里插入图片描述
  1. 临时空间的开辟,是在对应函数栈帧内部开辟的
  2. 函数调用完毕,栈帧结构被释放掉
  3. 临时变量具有临时性的本质:栈帧具有临时性
  4. 调用函数是有成本的,成本体现在时间和空间上,本质是形成和释放栈帧有成本
  5. 函数调用,因拷贝所形成的临时变量,变量和变量之间的位置关系是有规律的
    7.每个函数形成栈帧的指令都是一样,就是以下三条
    在这里插入图片描述
  • 16
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值