函数栈帧的创建和销毁

文章详细阐述了局部变量的创建过程,解释了为何未初始化的局部变量会有随机值。函数调用时,参数按照从右向左的顺序传递,形参是实参的临时拷贝。在函数执行完毕后,通过调整ebp和esp指针来销毁栈帧并返回。返回值通常存储在eax寄存器中,通过ret指令返回到调用者。
摘要由CSDN通过智能技术生成
  • 局部变量是怎么创建的?
  • 为什么局部变量的值是随机值?
  • 函数是怎么传参的?传参的顺序是怎么样的?
  • 形参和实参是什么关系?
  • 函数调用是怎么做的?
  • 函数调用结束后是怎么返回的?

这些和函数栈帧的创建和销毁有关,下面会慢慢解答:

对于函数栈帧的创建和销毁,编译器越高级,越不容易学习和观察。

函数栈帧的创建和销毁的过程在不同的编译器上是略有差异的,大体逻辑是一致的,具体细节取决于编译器的实现。
在这里插入图片描述

正在调用哪个函数,ebp和esp维护的就是哪个函数的函数栈帧
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在main函数调用的过程中:

(压栈push是给栈顶放一个元素,出栈pop是从栈顶删除一个元素)

push是压入元素
push完esp就会变化
然后esp的指向就会变成下图

在这里插入图片描述

move是把esp的值给ebp
在这里插入图片描述
sub是减,给esp减去0E4h
esp变小,也就是esp指向了上面的区域
在这里插入图片描述
push了 ebx esi edi之后
在这里插入图片描述
lea加载有效地址,相当于给edi里加了个地址
在这里插入图片描述
把39h放到ecx,eax里面放的是0CCCCCCCCh这样的值

然后下面的那句话的意思是:要把从edi这个位置开始向下的39h个dword(double word)(一个word就是2个字节,dword就是4字节)的数据全部改成eax的内容

每次操作4个字节,操作39h次,然后改成eax的内容
在这里插入图片描述
上面main函数的栈帧已经开辟好,然后准备执行有效代码
在这里插入图片描述
把0Ah这个十六进制数字(0A也就是十进制10)放到ebp-8的位置
在这里插入图片描述
在这里插入图片描述

如果没有对a初始化,那么默认就是那些CCCC…
之前我们经常能打印出随机值烫烫烫烫,就是因为内存中放的是CCCC…这样的值,不同编译器上放的随机值不一样,所以变量最好进行初始化

在这里插入图片描述
把14h这个十六进制数字放到ebp-14的位置
在这里插入图片描述
空了两个整形的位置在这里插入图片描述
别的编译器可能紧挨着放,因为这个空出的大小取决于编译器
在这里插入图片描述

把0放到ebp-20h的位置
在这里插入图片描述
在函数中局部变量是怎么创建的?
在这里插入图片描述
首先为我这次的函数调用创建函数栈帧,然后在它的函数栈帧里找到空间把a,b,c什么的放进去。

在这里插入图片描述
当函数调用时
把ebp-14h里的值放到eax里,就是把20放到eax里面
在这里插入图片描述
然后push eax
在这里插入图片描述
在这里插入图片描述
把ebp-8里的值放到eax里,就是把 a(10)放到ecx里面
然后push ecx,把10压到顶上在这里插入图片描述
在这里插入图片描述
call是调用函数,先记住call的地址在这里插入图片描述
调用之后:
在这里插入图片描述
00 c2 14 50

call这个动作把call的下一条指令的地址压到上面了
在这里插入图片描述

记住这个地址的原因是,call之后去调用add函数,跳到add函数里之后,调用完add函数之后还需要返回来,要回哪去?回到call指令的下一条指令的位置,然后再从这个地址往下执行

进入函数之后:
在这里插入图片描述
和main函数开辟函数栈帧的过程一样,是为add函数准备函数栈帧

push ebp之后:
在这里插入图片描述
在这里插入图片描述
move 把esp的值给ebp
在这里插入图片描述
然后sub esp减去0CCh,esp就指向了上面的空间
在这里插入图片描述
然后push 三个数:
在这里插入图片描述
然后往下就是像main函数一样把空间中的内存初始化为CCCCCCCC
在这里插入图片描述
准备执行计算
在这里插入图片描述
z的创建:把0放到ebp-8的位置
在这里插入图片描述
ebp+8找到ecx中的10放到eax
然后把eax中的20加到eax
在这里插入图片描述
eax中的值此时就为30了
然后把eax中的值放到ebp-8的位置,也就是z的位置

函数在调用计算的时候,形参不是主动创建的,是因为在调用函数的时候就把参数传过去了,参数从右向左传的在这里插入图片描述
形参根本不是在add函数内部创建的,而是回来找了在调用的时候传参压的那块空间在这里插入图片描述
有这样一句话:形参是实参的临时拷贝,在这里也得到验证

这时候到返回部分了:
在这里插入图片描述
把[ebp-8]的值放到eax里(也就是把z的值放到eax里),eax是一个寄存器,不会因为程序退出就销毁。因为z出函数后销毁后值就不在了,所以暂时把z的值放到寄存器里,等回到主函数再把eax的值拿出来用就可以了。

三次pop之后:
esp指向的位置就发生了改变
在这里插入图片描述
回收空间操作:把ebp赋给esp
esp就指向下面去了
在这里插入图片描述
然后pop ebp把栈顶元素弹出
栈顶元素存的是main函数的ebp也就是下图位置的地址
在这里插入图片描述
main-ebp存在栈顶的原因是:在函数调用返回之后随着函数栈帧的销毁,main函数的栈顶容易找到,但是main函数的栈顶就不记得了,所以先把main函数的栈顶存在main-ebp。
pop弹出把main-ebp的值弹到ebp,ebp就重新指向了main函数栈底的位置,同时esp往下,回到了main函数在这里插入图片描述

main函数这块空间又由ebp和esp维护了
从main函数回来之后,我们应该从call指令的下一条指令的地址往下执行
在这里插入图片描述
ret指令的作用就是:return返回的时候,这个指令把esp指向的那个call指令的下一条指令的地址弹出并且跳到那个位置在这里插入图片描述
在这里插入图片描述

esp+8之后就把x,y形参的空间释放了
在这里插入图片描述
在这里插入图片描述
把eax寄存器中的值放到ebp-20h,也就是放到c中
c=Add(a,b)返回值这时候带回来了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值