C C++最新从汇编代码探究函数栈帧的创建和销毁的底层原理_汇编栈帧,2024年最新拿下我人生中第7个Offer

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

push:给栈顶放一个元素

push          ebp 将ebp里面的值放在栈顶,同时因为esp指向的是栈顶,所以esp指针也‘上移’

如图

mov:将前面的值放到后面值的地方

mov        ebp,esp,将ebp指针移动到esp指针的位置

sub:前面的值减去后面的值

sub         esp,0E4h,指针esp减去0E4h(一个8进制的数字),并移动指针esp

由于ebp是栈底指针,esp是栈顶指针,正在调用哪个函数,ebp和esp就维护哪个函数的函数栈帧

后面调用Add函数的时候,ebp、esp就去维护Add函数的函数栈帧。

所以栈区中mian函数的调用空间(预开辟)就开辟好了

连续push三次,将这三个元素依次放到栈顶,压栈三次

同时esp也移动到栈顶

lea:load effecitive address   加载有效地址

lea             edi,[ebp-0E4h]  把  ebp-0E4h 的地址放到edi里面去,而ebp-0E4h应该就是main函数的栈顶地址

mov           ecx,39h 把39h的值放入eax中去

mov           eax,0CCCCCCCCh 把0CCCCCCCCh的值放入eax中

rep stos     dword ptr es:[edi] 把从 edi(edp-0E4h)开始向下ecx(39h)次 dword 的数据全部都改成 eax(0CCCCCCCC) (dword(双字节))图例

简单的来讲就要将从edp-0E4h 到 ebp 中间所有的内容初始化为0ECCCCCCCC

假设初始化完了(没有画全),意思就是中间全部初始化为0CCCCCCCC

此时main函数才开始执行代码

2.创建变量

在代码的反汇编部分,右击鼠标,将显示符号名关掉

一直按F10到Add之前的汇编代码处

mov         dword ptr [ebp-8],5       将5存入  ebp-8 的位置

mov         dword ptr [ebp-14h],2   将2存入  ebp-14h(4+1*16=20(ebp-20))的位置

mov         dword ptr [ebp-20h],0   将0存入  ebp-20h(0+2*16=32(ebp-32))的位置

存储之后的内存图

这样就创建了变量a,b,c

3.函数传参

mov         eax,dword ptr [ebp-14h]   将ebp-14h(其实就是b的值)的值放入eax(寄存器)里面去

push        eax  eax压栈,将eax放在栈顶

mov         ecx,dword ptr [ebp-8]   将ebp-8(其实就是a的值)的值放入ecx(寄存器)里面去

push        ecx  ecx压栈 ,将ecx放栈顶

传参—先传b,再传a (说明函数传参是从右向左的)

4.函数调用

call–函数调用

执行到此处要按F11进入函数内部

观察内存后,发现执行完call后,call指令的下一条指令的地址被压栈

当后面执行完Add函数后,通过这个地址可以找到调用位置并继续向下执行代码

再按F11就进入了Add函数,发现前面的指令与main函数里面一样,说明函数栈帧的创建过程是相同的

与刚开始创建mian函数栈帧的过程相同,唯一不同的就是不同函数栈帧开辟的空间大小有差异,这个空间大小取决于编译器

同样,由于正在调用Add函数,ebp和esp就来维护Add函数的函数栈帧

同样,在Add函数中开始执行代码

mov         eax,dword ptr [ebp+8]  ebp+8的值赋给eax

add         eax,dword ptr [ebp+0Ch]   将ebp+0Ch的值与eax的值相加并赋值给eax

mov         dword ptr [ebp-8],eax  将eax的值赋给ebp-8

eax是一个寄存器,可以存储变量/变量的值

说明真进行函数调用的时候,形参并不是在Add函数内部创建的,而是在函数调用刚开始的时候,通过压栈,将参数a,参数b传上去了,且参数b是先传的

在使用形参的时候,回头找到了以前压栈的这块空间,说明形参确实是实参的一份临时拷贝

形参并没有真再创建自己的空间

最后将形参计算的结果存入ebp-8中

但是如果在函数里面创建变量的话,它的内存是单独分配的

四、函数栈帧的销毁

 先将计算结果ebp-8(z)里面的值存在寄存器中,方便后面将Add的函数栈帧销毁后返回

三次pop,从栈顶删去三个元素

将ebp赋为esp(说明esp移动到ebp位置,此时栈顶发生变化,到了ebp的位置)

然后从栈顶删去一个元素放在ebp里面,esp继续向下。

因为此时栈顶放的是main函数的栈底地址,所以,ebp通过这个地址找到了main函数的栈底

此时就从Add函数里面顺利的回到了main函数里面

因为00F83420是调用函数的指令call的下一条指令的地址,所以恰好可以让代码继续运行

这就是上面为什么要将call下一条指令的地址存起来

**确保‘走出去’,还能‘找回来’**继续运行add这一条指令

然后ret(return):将栈顶的那个指针pop(出栈)

esp(栈顶指针)再向下移动

此时esp+8,即将栈顶 + 8就相当于将形参x,y销毁,如图

然后mov 将寄存器eax中存的Add函数的计算结果放在ebp-20h中,即c中

此时要进入printf函数,虽然printf已经封装好了,但printf函数的调用还是要经历函数栈帧的创建和销毁这个过程,方法与Add函数相同,这里就不过多赘述

那么return 0就是main函数栈帧的销毁过程

最后的结果就是main函数的栈帧也被销毁

在这里插入图片描述

学习新知识的过程大多是枯燥和乏味的,放弃很容易,但坚持一定很酷

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

1715727577585)]
[外链图片转存中…(img-EskfrQvk-1715727577585)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值