函数的栈帧与销毁(栈帧可不是战争哦)

 

🤩本文作者:大家好,我是paperjie,感谢你阅读本文,欢迎一建三连哦。

🥰内容专栏:这里是《C语言》专栏,笔者用重金(时间和精力)打造,将C语言知识一网打尽,希望可以帮到读者们哦。

🥴内容分享:本期会使用C语言知识来解释函数的栈帧与创建,力求让每一位读者看懂。

😘

:不要998,只要一件三连,三连买不了吃亏,买不了上当(写作不易,求求了💓)


目录

🍖前言

🍕理解需知

🫕常用的寄存器

🍘常用的汇编指令

🍿函数的栈帧与销毁

 🥙函数的创建

🫔main函数栈帧的创建

🍥main函数里面语句的执行

🥟 函数传参

🍤add函数栈帧的创建

 🥣add函数语句的执行

🍠函数的销毁

🍡add函数的销毁

🥞main函数的销毁

🍉总结


🍖前言

在C语言的学习过程中,我们会遇到许多问题,它是比较隐晦难懂的,但是这些好巧不巧有对以后的学习有着深远的作用。我们在学习完函数这一章后,有一些问题它是在许多书本上是不会讲解的,太过于接近底成。函数中:1局部变量是怎么创建的?2为什么局部变量的值是随机值?函数是怎么传参的?传参的顺序是怎么样的?3形参和实参是什么关系?4函数调用是怎么做的?5函数结束后是怎么返回的?这些问题大家肯定经常困惑,今天这篇函数的栈帧与销毁就是对这些问题进行讲解。

🍕理解需知

🫕常用的寄存器

  • eax: 通常用来执行加法,函数调用的返回值一般也放在这里面
  • ebx: 数据存取
  • ecx: 通常用来作为计数器,比如for循环
  • edx: 读写I/O端口时,edx用来存放端口号
  • esp: 栈顶指针,指向栈的顶部
  • ebp: 栈底指针,指向栈的底部,通常用ebp+偏移量的形式来定位函数存放在栈中的局部变量
  • esi: 字符串操作时,用于存放数据源的地址
  • edi: 字符串操作时,用于存放目的地址的,和esi两个经常搭配一起使用,执行字符串的复制

🍘常用的汇编指令

MOV     传送字或字节.  
MOVSX   先符号扩展,再传送.  
MOVZX   先零扩展,再传送.  
PUSH    把字压入堆栈.  
POP     把字弹出堆栈. 

PUSHA   把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈.  
POPA    把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈.  
PUSHAD  把EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI依次压入堆栈.  
POPAD   把EDI,ESI,EBP,ESP,EBX,EDX,ECX,EAX依次弹出堆栈.  
BSWAP   交换32位寄存器里字节的顺序  
CMPXCHG 比较并交换操作数.(第二个操作数必须为累加器AL/AX/EAX)  
XADD    先交换再累加.(结果在第一个操作数里)  

🍿函数的栈帧与销毁

为了方便演示,这里编写了一个加法函数,我们通过调试和观察它的反汇编来理解函数的栈帧与销毁。

 

 🥙函数的创建

 在开始前,我们了解两个小知识:函数和局部变量是在栈区中创建的,栈区的存放习惯是从高地址到低地址存放的。还是两个非常重要的寄存器ebp和esp,他们两叫栈底指针和栈顶指针,函数的栈帧就是有他们来控制的。(注意尽量使用低级的编译器,这样子观察比较容易,高级的封装的比较完善,不宜观察)

🫔main函数栈帧的创建

这里有一个问题,main函数,也是函数,那它也会被别人调用吗?答案是对的,我们可以通过调试中的调用堆栈来观察,果然我们发现的确有一个函数调用它,而这个函数又被另一个函数调用,用画图解释:

 这里我们回到正文,通过反汇编我们一步一步来解读函数(上面那个main函数的栈帧是一个简单的图,下面我们会详细来画)。

第一条指令是push一下ebp就是将ebp压入栈顶,因为esp是栈顶指针,这时esp就得向上移动一次,我们可以通过监视观察到esp移动了。

 

   mov    ebp,esp  和sub   esp,0E4h是说将esp赋给ebp,就是ebp的位置到了esp上。esp减去0E4h,这之间的空间就是main函数的栈帧开辟 

 00FB18B9  push        ebx   00FB18BA  push        esi  00FB18BB  push        edi  这三条指令就是将ebx,esi,edi压栈,随后esp向上移动三次。

00FB18BC  lea         edi,[ebp-24h]                 将ebp减去24h加载到edi中
00FB18BF  mov         ecx,9                            将9赋给ecx
00FB18C4  mov         eax,0CCCCCCCCh     将0cccccccch赋给eax
00FB18C9  rep stos    dword ptr es:[edi]       edi开始将0cccccccch9次向下赋给下面的单元空间

🍥main函数里面语句的执行

一直到这里算是main函数的栈帧完全开辟好了。接下来就是main函数里面语句的执行了。

    int a = 10;
00FB18D5  mov         dword ptr [ebp-8],0Ah    将10赋给ebp-8这个空间
    int b = 20;
00FB18DC  mov         dword ptr [ebp-14h],14h  将20赋给ebp-14这个空间

🥟 函数传参

    int c = add(a, b);
00FB18E3  mov         eax,dword ptr [ebp-14h]    将b的值赋给eax这个寄存器
00FB18E6  push        eax                                    将eax压栈,esp向上移动一次
00FB18E7  mov         ecx,dword ptr [ebp-8]        将a的值赋给ecx这个寄存器
00FB18EA  push        ecx                                    将ecx压栈,esp向上移动一次
00FB18EB  call        _add (0FB1023h)               用call指令调用add函数,且记住下一条指令

通过这些指令,我们可以发现函数传参是从右往左的

🍤add函数栈帧的创建

这里执行下一条语句的时候我们就要按F11进入add函数了

0FB1850  push        ebp  
00FB1851  mov         ebp,esp  
00FB1853  sub         esp,0CCh  
00FB1859  push        ebx  
00FB185A  push        esi  
00FB185B  push        edi  
00FB185C  lea         edi,[ebp-0Ch]  
00FB185F  mov         ecx,3  
00FB1864  mov         eax,0CCCCCCCCh  
00FB1869  rep stos    dword ptr es:[edi]  

这些画图板里有解释,我就不重复了

 

 🥣add函数语句的执行

    int z = 0;
00FB1875  mov         dword ptr [ebp-8],0  
    z = x + y;
00FB187C  mov         eax,dword ptr [ebp+8]  
00FB187F  add         eax,dword ptr [ebp+0Ch]  
00FB1882  mov         dword ptr [ebp-8],eax  
    return z;
00FB1885  mov         eax,dword ptr [ebp-8]  

 这里我们可以发现,形参x和y只是通过mov指令将a和b的值拷贝了一份放到eax寄存器中加起来放到了z中,返回的时候将z的值放回寄存器eax中,这样就不会因为函数的销毁数据丢失.

🍠函数的销毁

🍡add函数的销毁

00FB1888  pop         edi  
00FB1889  pop         esi  
00FB188A  pop         ebx  
00FB188B  add         esp,0CCh  
00FB1891  cmp         ebp,esp  
00FB1893  call        __RTC_CheckEsp (0FB1244h)  
00FB1898  mov         esp,ebp  
00FB189A  pop         ebp  
00FB189B  ret  

这里是通过pop和add指令将add函数的空间释放掉,让esp和ebp回到原来main函数的位置。

🥞main函数的销毁

00FB1909  pop         edi  
00FB190A  pop         esi  
00FB190B  pop         ebx  
00FB190C  add         esp,0E4h  
00FB1912  cmp         ebp,esp  
00FB1914  call        00FB1244  
00FB1919  mov         esp,ebp  
00FB191B  pop         ebp  
00FB191C  ret  

上面的指令和add函数销毁是一样的,这里我就不画图讲解了,可以根据上面的解释自行理解。


🍉总结

 现在,对于一开始的那些问题,相比大家就已经清楚了叭:

1局部变量是怎么创建的? 是函数栈帧创建后编译器分配,且是从高地址到低地址创建的

2为什么局部变量的值是随机值? 因为函数栈帧创建后编译器会将全部的内容都自动初始化一个值

3函数怎么传参?传参顺序是怎么样 传参将实参拷贝到寄存器中进行压栈到栈顶 顺序是从右到左

4形参和实参是什么关系?              形参是实参的临时拷贝,它们只是值相同,但是地址不同

5函数调用是怎么做的?                  通过call指令来调用

6函数结束后是怎么返回的              通过ret指令来返回

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16469.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法】最容易懂得的红黑树

红黑树是一个平衡的二叉树,但不是一个完美的平衡二叉树。虽然我们希望一个所有查找都能在~lgN次比较内结束,但是这样在动态插入中保持树的完美平衡代价太高,所以,我们稍微放松逛一下限制,希望找到一个能在对数时间内完…

PS学习笔记(零基础PS学习教程)

很多新手学习PS不知从何下手,做设计的第一阶段肯定是打牢基础,把工具用熟练;本期特别为大家整理了PS入门的学习笔记,把每个工具的用法整理了下来,在使用过程中有哪里不清楚的可以翻看来看看~ 一、ps的工作界面的介绍 …

Python程序员想要转行,可以从这几个方面着手

最近有很多朋友问我一个问题,不论是我们做程序员还是做产品经理或者其他行业,到了30岁或35岁之后,都会面临各种各样的问题,比如达到职业天花板。有没有一种方法能够解决这种问题呢?我想分享一下我的观点和身边的案例。…

网络攻击与防御

1.什么是数据认证,有什么作用,有哪些实现的技术手段? 数字认证证书它是以数字证书为核心的加密技术可以对网络上传输的信息进行加密和解密、数字签名和签名验证,确保网上传递信息的安全性、完整性。 使用了数字证书,即使您发送的…

ChatGPT是什么?ChatGPT里的G、P、T分别指什么

文章目录 ChatGPT是什么GTP中的 生成式 是什么意思GTP中的 预训练 是什么意思GTP中的 变换模型 是什么意思 什么是Transformer什么是注意力机制 监督学Xi、无监督学Xi、强化学Xi ChatGPT是什么 GPT: Generative Pre-trained Transformer 生成式预训练变换模型 ChatGPT是由Ope…

用ChatGPT问DotNet的相关问题,发现DotNet工程师的前景还不错

本人最近费了九牛二虎之力注册了一个ChatGPT账号,现在就给大家分享一下,问一下关于.NET的问题,看看ChatGPT的AI功能具体如何? 一、C#跟其它语言比较的优势 回答: C#是一门编程语言,它是为 Microsoft 的 …

第十三章 移动和旋转(上)

移动和旋转是游戏对象最频繁地操作。我们上个章节简单介绍了Cube的移动和旋转。移动是修改transform的position属性,旋转是修改transform的eulerAngles(欧拉角)属性,两者属性值均可以使用Vector3向量来实现。需要大家注意的是&…

B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能

B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能 文章目录 B/S 结构系统的 缓存机制(Cookie) 以及基于 cookie 机制实现 oa 十天免登录的功能每博一文案1. Cookie 的概述2. session 与 Cookie 之间的联系:3. Cookie 的作用&#xff…

盈泰德带你了解产品表面缺陷检测系统

与前几年相比,机器视觉行业在表面检测方面有了很大的突破。检测产品表面的划痕、污渍不再困难,广泛应用于金属、玻璃、手机屏幕、液晶面板等行业的表面检测。 机器视觉检测有以下四种常用的检查和照明方法: 同轴照明、低角度照明、背光照明…

Python一行命令搭建HTTP服务器并外网访问 - 内网穿透

文章目录 1.前言2.本地http服务器搭建2.1.Python的安装和设置2.2.Python服务器设置和测试 3.cpolar的安装和注册3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 转载自远程内网穿透的文章:【Python】快速简单搭建HTTP服务器并公网访问「cpolar内网穿透…

Java 基础进阶篇(五)—— 抽象类与模板方法设计模式

文章目录 一、抽象类、抽象方法概述二、抽象类的特征三、模板方法设计模式3.1使用场景3.2 实现步骤3.3 写作文案例 补充:final 和 abstract 是什么关系? 一、抽象类、抽象方法概述 在 Java 中 abstract 是抽象的意思,可以修饰类、成员方法。 abstract …

win10远程桌面控制Ubuntu服务器 - 内网穿透实现公网远程

文章目录 前言视频教程1. ubuntu安装XRDP2.局域网测试连接3. Ubuntu安装cpolar内网穿透4.cpolar公网地址测试访问5.固定域名公网地址 转载自远程穿透文章:Windows通过RDP异地远程桌面Ubuntu【内网穿透】 前言 XRDP是一种开源工具,它允许用户通过Windows…

机械硬盘(HDD)与固态硬盘(SSD)

目录 机械硬盘(HDD) 最小组成单元是扇区 硬盘结构 硬盘工作原理 硬盘上的数据组织 硬盘指标 影响性能的因素 固态硬盘(SSD) 最小存储单元是Cell SSD的特点 SSD架构 NAND Flash 闪存介质 地址映射管理 FTL闪存转换层 机械硬盘&…

JAVA IO 模型详解

什么是IO I/O(Input/Outpu) 即输入/输出 。 从计算机结构的视角来看的话, I/O 描述了计算机系统与外部设备之间通信的过程。 从应用程序的视角来看的话,我们的应用程序对操作系统的内核发起 IO 调用(系统调…

微信小程序定义模板

微信小程序提供模板(template)功能,把一些可以共用的,复用的代码在模板中定义为代码片段,然后在不同的地方调用,可以实现一次编写,多次引用的效果。 首先我们看一下官网是如何操作的 一般的情…

JavaWeb学习--RequestResponse

目录 JavaWeb学习--Request&Response 1,Request和Response的概述 request:获取请求数据 response:设置响应数据 **小结** 2,Request对象 **小结** 2.2 Request获取请求数据 **小结** 2.4 请求参数中文乱码问题 URL编码 2.5 Request请求转…

【前端技术】Vue3 01:初识 Vue.js

Vue 可以说是非常流行了,至少在国内是这样,他是个轻量级的 JavaScript 框架,非常适合构建大型和中小型的 Web 应用程序,如果想和前端打交道,应该绕不过这个框架吧。 目录 1 Vue.js 介绍 2 IDE 选择 2.1 vscode 2.…

Eplan 部件库导入部件的方法

1. 部件宏文件如何下载 1.1 西门子部件宏文件下载 EPLAN 的部件库是可以更新的,一般元器件厂商会提供其部件文件,以 SIEMENS 为例 进入网站,点击EPLAN 的图标 https://www.automation.siemens.com/bilddb/index.aspx?lang=en 在订货号中输入所需部件订货号,点击搜索。点…

【Java笔试强训 27】

🎉🎉🎉点进来你就是我的人了博主主页:🙈🙈🙈戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔🤺🤺🤺 目录 一、选择题 二、编程题 🔥 不用加…

sed编辑器基础命令

shell脚本编程系列 学习sed编辑器 sed编辑器被称作流编辑器(stream editor),与普通的交互式文本编辑器不同,在交互式文本编辑器可以用键盘命令交互式插入、删除或替换文本数据。流编辑器则是根据事先设计好的一组规则编辑数据流。 sed编辑器…