函数栈帧的创建和销毁介绍

简介: 函数栈帧的创建和销毁介绍




首先理解一下寄存器:

eax,ebx,ecx,edx,ebp,esp。画横线的这两个寄存器存放的是地址。这两个地址是用来维护函数栈帧的。

每一次函数调用,都要在栈区创立一个空间。

什么是栈?

函数通过栈来实现控制转移、参数传递、局部变量的分配和释放3个功能。 计算机有专门的一块内存区域作为栈,每个函数都可以在栈上申请一块内存区域作为函数的存储空间,而该存储空间则被称为函数的栈帧。

栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈,push),也可 以将已经压入栈中的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据后出栈

编写代码

详细解释栈帧创立和销毁过程

如下图所示,在栈区(计算机专门的内存空间),每个函数在栈区申请一块内存空间,称为函数栈帧。在调用哪个函数,esp和ebp就跑去维护哪个函数的栈帧。我们通常把ebp称为栈底指针,esp称为栈顶指针。栈区的使用情况是先使用高地址,再使用低地址(向下增长)。

点调试窗口。按下图进行操作。

我们在函数执行完的时候,可以在调用堆栈中看到:

main函数被__tmainCRTStartuo()调用。

而上面的函数又被上图中下面一个函数调用。

esp和ebp首先维护main函数,再调用add函数。

此时ebp,esp维护的是这样一个空间。进入main函数的第一步是push。push压栈,栈里面放的是ebp。当push完成之后,esp指到栈顶。

我们也可以在监视中观察到:1

2

我们可以观察到esp的地址 减少了4,意味着esp往上了4位。

接着执行move,move是把esp的值给ebp。那么ebp就不再指向原来的地方了。

同样的,也可以在监视窗口中看到

接着执行sub,给esp减去0E4h(16进制数字)。意味着esp指向上面的某一块位置。

这块空间就是为main函数申请的空间。紧接着,push三次。顶上压了个元素ebx,push完之后esp向上指。执行完成之后,在顶上压了三个元素。

lea:load effective address   加载有效地址

接着,lea,ebp-0E4h

接着执行到rep stos,把39h里的双字(4个字节)全部改为eax的内容0CCCCCCCCh 。

到ebp结束。往上所有的空间都初始化为cccccccc。

为啥不初始化打印出来的就是烫烫烫烫呢?

是因为main函数调用时,在栈区开辟的空间的其中每一 个字节都被初始化为0xCC,而arr数组是一个未初始化的数组,恰好在这块空间上创建的,0xCCCC(两 个连续排列的0xCC)的汉字编码就是“烫”,所以0xCCCC被当作文本就是“烫”。

接着执行的语句让ebp-8中放入了一个10。(与上一个数值之间差了两个整型)

接着调用add函数,

接着在栈顶压20。

mov 把ebp-14h放到eax里。也就是把20的值放到eax里面去了,然后push eax,eax压栈,里面存放的是20。然后move,把 ebp-8的值放到了ecx当中。push,顶上又压了一个ecx10。

接下来call指令调用函数,按F11。call指令又把add函数的地址压到顶上来了。(call指令的下一条指令的地址)

然后来到了add函数

前面这一堆和原先的函数内容一样。

sub,给esp减去一个0CCh

然后又是三次push。edi到ebp之间的所有空间全部初始化为CCCCCCh。

ebp+8

ebp+8把a撇b撇找过来了。把ebp+8的值加到 eax里,再把ebp+12的值加到eax里。再把算出的结果30放到ebp-8里面去。我们可以发现参数是从右向左传的。形参不是在add函数内部创建的,而是找到刚刚传参压过去的空间。a和b就会分别被认为是x和y。在没有调用add函数时,参数就已经传过去。我们可以说,形参是实参的一份临时拷贝。改变形参a撇b撇,不改变实参。

最后一步return z,z是怎么返回的呢?把ebp-8的值放到eax里面去。eax是个寄存器,寄存器是不会退出就销毁的。ebp-8就是z,里面存放着30的值。等回到主函数时,再把eax的值拿出来用就行了。

三次pop弹出 再把ebp赋给esp。然后再pop一下,把栈顶的元素弹出来,栈顶弹的是main函数的ebp。ebp的地址存在main函数当中,就是要让随着函数调用返回之后,随着栈帧的销毁,栈顶是很容易找到的,但是栈底不容易找到。pop弹出,ebp走了。

ebp就回回去了。pop一下找到了main函数的栈帧空间。

这样就顺顺利利地回到了main函数里头了,还应该从call指令的下一条指令执行。顶上放着call指令下一条指令的地址,在栈顶存这个地址就是为了在函数调用完之后还能回来,回来之后还能从call指令的下一条指令往下执行。在函数调用完成之后,形参x和y就没有作用了

ebp+8。esp指向移动8个字节,上面的空间不属于。再把eax的值放到ebp-20h当中。eax的值就是出add函数时委托到eax当中的和,和放到局部变量c当中,这样返回值就带回来了。

解决疑惑

局部变量是如何创建的?

首先为函数分配好栈帧空间,栈帧空间初始化好一部分空间之后,然后给局部变量在栈帧里分配一点空间。

为什么局部变量不初始化内容是随机的?

随机值是被随机放入的。如果初始化,就相当于把随机值覆盖了。

函数调用时参数时如何传递的?

当没有调用函数的时候已经pushpush把两个参数从右向左开始压栈压进去了,当真的进入形参函数的时候,其实在add函数栈帧里,通过指针的偏移量找回了形参。

函数的返回值是如何带会的?

调用之前就把call指令的下一条指令的地址记住了,当往回返的时候,就可以跳转到call指令下一条指令的地址,返回值是通过寄存器的方式调用回来的。

欢迎交流!

相关文章
|
存储 NoSQL Redis
redis-(error)-MISCONF。Redis。is。configuredto。save。RDBsnapshots
redis-(error)-MISCONF。Redis。is。configuredto。save。RDBsnapshots
1149 0
|
12月前
|
供应链 安全 物联网
NFC(近场通信)技术及其工作原理详解
NFC(近场通信)技术及其工作原理详解
3642 11
|
弹性计算 大数据 测试技术
阿里云服务器价格购买价格表,一目了然!
阿里云服务器价格购买价格表,一目了然!2024年阿里云服务器租用价格表更新,云服务器ECS经济型e实例2核2G、3M固定带宽99元一年、ECS u1实例2核4G、5M固定带宽、80G ESSD Entry盘优惠价格199元一年,轻量应用服务器2核2G3M带宽轻量服务器一年61元、2核4G4M带宽轻量服务器一年165元12个月、2核4G服务器30元3个月
1751 0
|
网络协议 安全 网络安全
免费申请 HTTPS 证书的八大方法
免费申请 HTTPS 证书的八大方法
11225 0
|
负载均衡 Java 微服务
Java错误:com.netflix.client.ClientException: Load balancer does not have available server for client
Java错误:com.netflix.client.ClientException: Load balancer does not have available server for client
|
域名解析 网络协议 应用服务中间件
阿里云SSL证书配置(HTTPS证书配置)
该内容是一个关于如何在阿里云上准备和购买SSL证书,以及如何为网站启用HTTPS的步骤指南。首先,需要注册并实名认证阿里云账号,然后在SSL证书控制台选择证书类型、品牌和时长进行购买。申请证书时填写域名信息,并进行DNS验证,这包括在阿里云域名管理板块添加解析记录。完成验证后提交审核,等待证书审核通过并下载Nginx格式的证书文件。最后,将证书配置到网站服务器以启用HTTPS。整个过程涉及账户注册、实名认证、证书购买、DNS设置和证书下载及安装。
7186 1
|
JavaScript 前端开发 Go
JavaScript 02 BOM编程和函数
JavaScript 02 BOM编程和函数
134 0
JavaScript 02 BOM编程和函数
|
存储 算法 数据库
人脸识别数据集
人脸识别数据集
|
Web App开发 测试技术 PHP
|
JavaScript API 定位技术
【百度地图API】自行获取区域经纬度的工具
原文:【百度地图API】自行获取区域经纬度的工具 摘要:上一章教大家如何建立自己的行政区域地图。这次为大家提供一个,可视化选择区域,并且能自动生成经纬度代码的工具。工具的源代码完全公开,并且做了详尽的注释。
1783 0