函数栈帧的形成与释放

✅作者简介:嵌入式入坑者,与大家一起加油,希望文章能够帮助各位!!!!
📃个人主页:@rivencode的个人主页
🔥系列专栏:玩转C语言
💬推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站进行注册学习

1.栈区的特点
在这里插入图片描述
栈区是从高地址向低地址方向生长:高地址是栈底,低地址是栈顶,也使用高地址空间在使用低地址空间。

在这里插入图片描述
但由起始地址(地址最低的地址),存放变量的字节地址是顺序且递增的

在这里插入图片描述
这就是为什么数组的元素是地址是递增的,虽然栈是从高地址向低地址方向生长,但是数组是整体在栈上开辟空间,数组的其他元素的地址是依次递增

2.函数栈帧

在讲函数栈帧之前先看常用的汇编指令,和寄存器
在这里插入图片描述
接下来就以main函数调用Add函数为例,详细阐述调用Add函数,形成函数栈帧,函数调用完毕释放栈帧的详细过程,其中涉及到形成临时变量,形成函数栈帧,函数结束如何销毁栈帧,如何返回到调用Add函数的main中继续执行后面的代码。
在这里插入图片描述
vs2013 有栈随机化的处理(有关数据的地址可能不一样),重新运行代码可能会导致,每次看到的相关数据可能会不太一致,不过我们重点关注变化原理和过程,

先看一下函数调用的整个过程

请添加图片描述
接下来就是逐段代码详细讲解

main函数也是被其他函数调用
在这里插入图片描述

main函数被其他函数调用,则形成main函数的栈帧(在栈分配一块内存)。

在这里插入图片描述
在这里插入图片描述
这里如何形成的main栈帧,等讲完main函数调用Add函数形成Add函数的栈帧过程就理解了。

在这里插入图片描述
int x =0xA对应的汇编代码执行前
在这里插入图片描述
int x =0xA对应的汇编代码执行后
在这里插入图片描述
int y =0xB 与 int z =0 执行完之后,与上面过程类似
在这里插入图片描述

三个变量分配内存并初始化。
在这里插入图片描述

形成x,y的临时变量a,b(形参)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

总结:

在这里插入图片描述
1.临时变量的形成是在函数正式被调用就形成了的
2.形参实例化的顺序是从右向左

在这里插入图片描述
接下来开始调用Add函数
在这里插入图片描述
在这里插入图片描述
函数调用包含两个
1.压入返回地址(入栈返回地址)
2.转入目标函数

第二好理解,要调用Add函数肯定要转入Add函数中执行Add函数的代码,但等函数调用完毕得返回main函数中在继续执行后续代码,所以必须保存返回地址->返回main函数调用Add函数的下一条指令。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
返回地址入栈,栈顶上移
在这里插入图片描述

接下来就是创建Add函数栈帧,但在这之前要线存储main函数栈底地址保存下来(入栈),因为Add函数调用完毕之后,销毁栈帧,此时得栈底指针ebp与栈顶指针esp要重新指向main函数栈帧栈底与栈顶,所以必须提前保存main函数栈帧栈底地址。
在这里插入图片描述
先保存main函数栈帧栈底地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
接下来就是形成Add函数的栈帧
第一步:
在这里插入图片描述
在这里插入图片描述
第二步
在这里插入图片描述
在这里插入图片描述

总结:
在这里插入图片描述
当函数被调用时,即Add函数被调用时,编译器就会自动形成Add函数栈帧,至于函数的栈帧大小,编译器也会自己根据函数中变量与变量的类型来估计函数栈帧的大小,总之一句话函数栈帧由编译器搞定

Add函数栈帧开辟成功后,则开始执行Add函数中的代码,实现变量分配内存并初始化及数据运算。
在这里插入图片描述
int c =0 对应的汇编代码执行前

在这里插入图片描述
int c =0 对应的汇编代码执行后
在这里插入图片描述
此时将c变量分配空间并初始化为0
在这里插入图片描述
接下来就是进行加法运算
在这里插入图片描述

在这里插入图片描述

ebp+8就是保存的0xA也就是a变量
ebp+c就是保存的0xB也就是b变量

在这里插入图片描述
在这里插入图片描述
此时就完成了c=a+b
在这里插入图片描述
最后返回的时候将c的值写入eax临时寄存器中,也就是说Add函数的返回值是通过CPU中的临时寄存器返回
在这里插入图片描述
接下来就是函数调用完毕,释放Add函数的栈帧,返回main函数中执行,栈底指针ebp与栈顶指针esp重新指向main函数的栈帧栈底与栈顶
在这里插入图片描述
在这里插入图片描述

下图这句代码相当于释放Add函数的栈帧
在这里插入图片描述

在这里插入图片描述
使栈底指针ebp重新指向main函数栈帧栈底
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时eip寄存器拿到栈顶的返回地址,则即可返回main函数中执行main函数后续代码
在这里插入图片描述
回到main函数中,直接执行
add esp,8 即让esp后移8个单位,即释放原来的临时变量

在这里插入图片描述
自此Add函数栈帧销毁,所以入栈元素全部销毁包括原来入栈的两个临时变量
在这里插入图片描述
接下来就是接收返回值,前面已经讲了返回值c变量的值已经存储在eax临时寄存器,现在将eax中的返回值0x15,移动到ebp-20h中而ebp-20h内容就是z变量,相当于将返回值放入z变量中
在这里插入图片描述
在这里插入图片描述
至此整个调用Add函数创建栈帧,执行完毕后释放栈帧过程全部完毕

其他函数的调用也就类似,main函数也是被其他函数调用,当main函数被调用时,编译器自动形成main函数栈帧,等main函数执行完毕后也会释放main的栈帧

总结:
函数调用返回的整个过程
在这里插入图片描述
1.调用函数,需要先形成临时拷贝,形成过程是从右向左的
2.临时空间的开辟,是在对应函数栈帧内部开辟的,函数调用完毕,栈帧结构被释放掉,因此函数中的变量的空间也随之释放,所以临时变量具有临时性。
3.调用函数是有成本的,成本体现在时间和空间上,本质是形成和释放栈帧有成本
4. 函数调用,因拷贝所形成的临时变量,变量和变量之间的位置关系是有规律的

在这里插入图片描述

  • 24
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 15
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rivencode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值