函数栈帧的创建和销毁——修炼我们的内功

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(更新ing)

在这里插入图片描述


前言
我们在学习c语言的过程中一般都是学习它的语法,里面的一些函数,也就是说我们一般是作为使用者去使用这些东西,但是使用工具容易,代码运行的底层原理我们却还是一窍不通,就比如下方的几个问题,你能搞明白它的来龙去脉吗?

1.局部变量是怎么创建的?
2.为什么局部变量的值是随机值
3.函数是怎么传参的?传参的顺序是怎么样的?
4.形参和实参是什么关系?
5.函数调用是怎么做的?
6.函数调用是结束后怎么返回的?

是不是一下子被干懵了?其实不止是你们,我一开始也被搞的云里雾里,所以,这一期我们将从底层出发,去深层剖析代码运行的过程中的一些来龙去脉,修炼我们基本的内功。
而本期将以下方这部分的代码解析代码在底层的运行。
在这里插入图片描述


👉🏻寄存器

寄存器:存放一些数据的地方,你可以认为寄存器就是你的口袋。身上只有那么几个,只装最常用或者马上要用的东西。
常见的寄存器:eax,ebx,ecx,edx,eip esp,ebp
eax:是累加器,它是很多加法乘法指令的寄存器
ebx:是“基地址”寄存器,在内存寻址时存放基地址
eip:指令寄存器,保存当前指令的下一条指令的地址
esp,edp:这两个寄存器中存放的是地址,这2个地址是用来维护函数栈帧的,esp所指向的地址称为是栈顶指针,edp所指向的地址称为是栈底指针,栈顶和栈底所包含的区域为所要维护的函数栈帧

👉🏻函数栈帧的维护

我们都知道,每一个函数调用,都要在栈区创建一个空间,这个空间就是我们为这个函数在内存中开辟出来的栈帧空间而在调用这个函数时,我们需要对这个栈帧空间进行维护,而谁来维护呢?那就是在上文中我们讲过的esp和edp,而这里我们以调用main函数为例,看看是如何在内存中创建栈帧空间并维护的。
在这里插入图片描述

我们一看图,可以看到main函数在内存中开辟的函数栈帧。
在这里插入图片描述
但是奇怪的是,为什么下方又多出了_tmainCRTStartup和mainCRTStartup()这两个神马,这是什么呢?其实我们在调用main函数的时候,其实main函数是被_tmainCRTStartup函数调用的,而_tmainCRTStartup函数又被mainCRTStartup()函数所调用,所以在开辟栈帧空间的时候,我们要先开辟_tmainCRTStartup和mainCRTStartup()的栈帧空间,才轮得到main函数开辟它的栈帧空间。
但是栈帧空间又是怎么被开辟的呢?下面我们将通过汇编语言来察看这里面的细节,但是在开始前,我们需要先了解汇编语言的一些基本指令

👉🏻 汇编指令

1.push:我们称之为压栈,即在栈顶上放置一个元素,作用为移动esp的指向(让esp向上移动一个单位)
2.pop:出栈,从栈顶移去一个元素,作用为移动esp的指向(让esp向下移动一个单位)
3.mov:给数据移动
4.sub:执行减法
5.add:执行加法
6.lea(load effective address):加载有效地址
7.rep:重复指令
8.stos:把寄存器中的值拷贝到指定地址处
9.call:函数调用指令,将程序下一条指令的地址压入堆栈中,并转移到所调用的子程序。
10ret:终止当前程序的执行,返回call指令的下一条指令的地址

👉🏻栈帧的创建过程

我们将代码进入调式状态,右键点击转到反汇编,来查看反汇编里面的内容。
在这里插入图片描述
如图红色方框中,我们可以看到在建立main函数栈帧之前,执行了这么一段代码,那么这段代码到底是怎样执行的呢?接下来我给大家逐行分析
1.push ebp :
此时将ebp的值(地址)压栈到_tmainCRTStartup栈帧的栈顶ebb此时指向的是_tmainCRTStartup栈帧栈底,所以ebb的值为栈底的地址
在这里插入图片描述
而后esp移动4个字节(4个单位)来到ebp的上方,而在监视中,我们也可以看到esp的地址发生了变化——减4.
在这里插入图片描述
2.mov ebp,esp :即把esp中的值(地址)赋给ebp,而这意味着什么呢?意味着原边ebp是指着_tmainCRTStartup栈帧的栈底,现在指着esp所在地址的位置
在这里插入图片描述
3. sub esp,0E4h :sub也就是将esp的值减去0E4h(这个值是多少不用搞懂,在调试中可以看到到底退了多少),因为在地址排放时,是由高地址向低地址排放,所以当esp减小时,也就是此时esp指向低地址处的某一地址。
在这里插入图片描述

在这里插入图片描述
4-6. push ebx/eso/edi :压栈过程,esp继续移动
在这里插入图片描述

7.lea edi,[ebp-24h] :此时将ebp此时的地址-24h得到的地址加载到edi中
8-9.mov ecx,9 |mov eax,0CCCCCCCCh :即将9这个值存放到ecx寄存器中,0CCCCCCCCh存放到eax这个寄存器中
10.rep stos dword ptr es:[edi] :rep为重复指令的意思,ecx中的值为重复次数,dword就是double word,也就是双字,[edi]即edi此时地址下的范围,总体意思stos每次拷贝双字到范围edi(ebp-24h)~ebp的区域里。
在这里插入图片描述

在这里插入图片描述
而为什么要在main函数栈区里面填充cccc呢?我们可以拿变量来说明,如果一个变量没有初始化,那么最后它的输出值会是多少呢?就是由这些cccc决定的,是随机的。

👉🏻局部变量的创建

函数栈帧创建好之后,接下来终于轮到创建局部变量了
在这里插入图片描述

  1. mov dword ptr [ebp-8],0Ah:这里将0Ah(若将汇编语言变成10进制数,就是10)传入ebp-8这个地址中
  2. mov dword ptr [ebp-14h],14h :同理1
  3. mov dword ptr [ebp-20h],0 :同理1
    在这里插入图片描述

👉🏻函数如何传参

在这里插入图片描述

  1. mov eax,dword ptr [ebp-14h] :将 [ebp-14h]中的值即20放到eax寄存器中
  2. push eax:压栈eax,esp移动所以就有👇🏻👇🏻
    3. **加粗样式**

3.mov ecx,dword ptr [ebp-8] :同理1
4. push ecx :同理2
在这里插入图片描述
1-4的流程下来完成了初步的传参.
5. call 00B31023 :压栈call指令的下一个指令的地址,这一步是为了什么呢?因为下一步就要进入到add函数中,留下call下一个指令的地址方便待会回来继续执行的时候能够找到。按f11进入add函数

👉🏻函数调用是怎么做的

add函数中的反汇编
在这里插入图片描述
在执行该汇编语言之前,现在的main函数的栈区样子
在这里插入图片描述
解析来这一段汇编语言与main函数中大致相同,所以我就不赘述了,直接给结果图
在这里插入图片描述

  1. push ebp :
    在这里插入图片描述
    2.mov ebp,esp :
    在这里插入图片描述
    3.sub esp,0CCh :
    在这里插入图片描述
    4-6:
    在这里插入图片描述
    7.rep stos dword ptr es:[edi] :
    在这里插入图片描述

在这里插入图片描述
mov dword ptr [ebp-8],0 :
在这里插入图片描述
然后接下来编译器的操作就十分巧妙
在这里插入图片描述
直接将高地址中a和b的值即10和20取了过来,放到eax内存器中,然后eax的值再放入[ebp-8]中。原本的0值变为30,而这就直接完成了加法的功能,所以为什么说形参是实参的拷贝,这是完全正确的。然后此时再将这个30的值再存进eax内存器中,而这就是最后要求的z值,然后要将这个值返回到main函数中
接下来函数已经调用完毕,要开始返回了main函数了。

👉🏻函数调用结束后怎么返回的

在这里插入图片描述
在这里插入图片描述
此时ret在该程序结束时,返回到了call指令的下一条指令,也就是main函数中add函数,
在这里插入图片描述
而在这里,eax又将值传给了[ebp-20h]而这个地址,其实就是c所在的地址,c得到了30的值。
就此,我们终于完成了整个过程,才得到屏幕上打印的30.
在这里插入图片描述

1.局部变量是怎么创建的?
答:在所开辟的函数栈帧上开辟自己的栈帧空间
2.为什么局部变量的值是随机值
答:因为编译器在局部变量所在的函数栈帧中输入的就是随机值,所以如果不初始化,结果就是随机值
3.函数是怎么传参的?传参的顺序是怎么样的?
答:所谓形参,就是去寻找到实参的地址,然后将实参的值拷贝到身上,顺序是从右到左,假设a,b为正序,因为在栈帧上,b在a的上面,而形参回溯过来找地址时,先找到b
4.形参和实参是什么关系?
答:形参是实参的拷贝,形参的改变不会影响到实参。
5.函数调用是怎么做的?
答:以原函数的栈顶作为调用函数的栈底指针,为调用函数开辟新的栈帧,然后用call调用函数
6.函数调用是结束后怎么返回的?
答:ret指令到达call指令下方的指令,也就是原函数中未执行完的代码行继续进行执行,然后带着寄存器中所保留的值return回来。

本期的内容实在有点抽象,如果哪里不对希望得到大家的斧正🫡。


今日好诗
《夜书所见》
[宋] 叶绍翁
萧萧梧叶送寒声,江上秋风动客情。
知有儿童挑促织,夜深篱落一灯明。

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值