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

news2024/10/1 19:22:59

 

🤩本文作者:大家好,我是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指令来返回

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

码出高效:Java开发手册笔记(java对象四种引用关系及ThreadLocal)

码出高效:Java开发手册笔记(java对象四种引用关系及ThreadLocal) 前言一、引用类型二、ThreadLocal价值三、ThreadLocal副作用 前言 “水能载舟,亦能覆舟。”用这句话来形容 ThreadLocal 最贴切不过。ThreadLocal 初衷是在线程并…

《C和指针》笔记3:置一和清零

在C语言中&#xff0c;移位运算符<<和>>表示二进制数左移或右移&#xff0c;我们可以利用它们很轻松地对变量中单个位进行置一和清零。 我们可以规定变量二进制数最右边一位是第0位&#xff0c;定义bit_number是0&#xff0c;对应地&#xff0c;从最右边一位往左数…

iOS开发系列--打造自己的“美图秀秀”

--绘图与滤镜全面解析 概述 在iOS中可以很容易的开发出绚丽的界面效果&#xff0c;一方面得益于成功系统的设计&#xff0c;另一方面得益于它强大的开发框架。今天我们将围绕iOS中两大图形、图像绘图框架进行介绍&#xff1a;Quartz 2D绘制2D图形和Core Image中强大的滤镜功能…

【OpenVinoSharp】基于C#和OpenVINO部署PaddleOCR模型

基于C#和OpenVINO部署PaddleOCR模型 1.OpenVINO2.PaddleOCRPP-OCRv2PP-OCRv3 3.模型下载与转换3.1 Detection model3.2 Direction classifier3.3 Recognition model 4.OpenVinoSharp安装4.1 获取OpenVinoSharp源码4.2 配置OpenVINO依赖项 5. PaddleOCR5. 1模型推理C#实现&#…

Kali 安装中文输入法(超详细)

1.进入管理员下的控制台。 2. 输入密码后点击“授权”。 3.在控制台内输入下面的命令。 apt install fcitx 4.敲击回车后会开始安装&#xff0c;这里输入“y”。 5.回车后会继续进行安装&#xff0c;安装完成后会自动停止。 6.输入下面的命令来安装google输入法。 apt-get in…

【Python】【进阶篇】22、Django模板加载与响应

目录 22、Django模板加载与响应1. 什么是模板1) 模板的配置2) 修改settings配置文件 2. 模板的加载与响应方式3. render方法详解 22、Django模板加载与响应 在前文章节《Django模板系统》中&#xff0c;我们对 Django 的模板系统有了初步的认识&#xff0c;在本章我们将重点讲…

mongodb v4.4.19 分片精进实操,吐血分享

第一步&#xff1a;创建文件夹&#xff0c;data1111存放config server,data2222/data3333存放shard 第二步&#xff1a;启动服务 2.1 启动config server 2.2 启动shard --port 设置端口 --dbpath 设置数据库路径 --logappend 使用追加的方式写日志 --replS…

ReentrantLock原理剖析

前言 本文主要讲解底层逻辑&#xff0c;基本不会贴代码&#xff0c;目的是让大家能够真正的知晓原理&#xff0c;对照着逻辑去理解代码看代码也会很快就能看懂。 在讲ReentrantLock原理之前&#xff0c;我们先回顾下ReentrantLock的基本用法。ReentrantLock是一个锁编程api&am…

Compiler Lab2- 自制极简编译器

笔者实现的这个超级迷你版编译器&#xff08;词法分析、语法分析、生成中间代码&#xff08;cpp&#xff09;&#xff09;仅支持四则运算&#xff0c;功能真的是非常非常简单&#xff0c;不过其中的流程&#xff08;词法分析 -> 语法分析 -> 中间代码生成&#xff09;还是…

各SQL引擎的SQL转换过程对比

SQL引擎 参考文档:高级语言的解析过程—解析树 从 MySQL、Oracle、TiDB、CK,到 Hive、HBase、Spark,从关系型数据库到大数据计算引擎,他们大都可以借助 SQL 引擎,实现 “接受一条 sql 语句然后返回查询结果” 的功能。 他们核心的执行逻辑都是一样的,大致可以通过下面…

【五一创作】《嵌入式系统》知识总结7:GPIO寄存器

总述 每组端口具有7个寄存器 • 实现对GPIO端口初始化配置和数据输入输出控制 1. 配置寄存器&#xff1a;GPIOx_CRL、GPIOx_CRH 用来选择引脚功能&#xff0c;例如输入或输出 2. 数据寄存器&#xff1a;GPIOx_IDR、GPIOx_ODR 用来保存引脚输入电平或输出电平 3. 位控寄存器…

数据库管理-第七十一期 五一,休息?(20230503)

数据库管理 2023-05-03 第七十一期 五一&#xff0c;休息&#xff1f;1 备份2 两个DDL3 问题处理4 问题排查总结 第七十一期 五一&#xff0c;休息&#xff1f; 好不容易&#xff0c;熬过万恶的6天班来到了五一假期&#xff0c;想着好好休息&#xff0c;顺便把绝地幸存者给通关…

Java 基础进阶篇(九)—— 常用 API

文章目录 一、Object 类二、Objects 工具类三、Math 类四、System 类五、BigDecimal 类 一、Object 类 一个类要么默认继承了 Object 类&#xff0c;要么间接继承了 Object 类&#xff0c;Object 类是 java 中的祖宗类。Object 类的方法是一切子类都可以直接使用的。 因此&…

Obsidian +Obsidian Git插件 + Gitee 自动同步笔记

在Obsidian 关闭安全模式 然后再插件市场里面搜索并下载Obsidian Git 这个插件 注意&#xff1a;这里需要科学上网才能搜索并下载&#xff0c;我看很多博主都没说这件事情 然后再你的Gitee中&#xff0c;新建一个仓库 把这两个勾选上&#xff0c;然后随便选个语言&#xff0…

【JavaEE】应用层UDP协议

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 本篇文章将为大家介绍应用层中UDP协议~~ 在应用层这里&#xff0c;虽然存在一些现有的协议&#xff08;HTTP&#xff09;&#xff0c;但是也有很多情况&#xff0c;需要程序猿自定制协议&a…

Windeployqt 打包,缺少DLL 的原因分析,解决方法

很多同学使用工具windeployqt进行打包发布后&#xff0c;运行exe文件时&#xff0c;还是会出现下图所示的系统错误提示&#xff0c;这种情况就表示相关的DLL 库文件没有被正确打包。可是windeployqt明确显示运行正常啊&#xff0c;难道是QT自家的windeployqt这个工具有bug&…

2024年浙大MBA创客班项目提面如何申请?

2024年浙大MBA创客班提面如何申请&#xff1f;在目前的提前批面试申请过程中不少考生都不太清楚&#xff0c;专注浙大的杭州达立易考教育本期将项目的提前批面试和常规批复试申请基本流程和关键信息整理出来&#xff0c;帮助考生更精准的进行面试准备。 一、浙大MBA创客班项目…

基于比较排序算法总结

1.数据无法全部放入内存 2.数据以数据流形式

操作系统2(多处理器编程)

一、并发 1.操作系统是最早的并发程序之一 2.并发的基本单位&#xff1a;线程 共享内存的多个执行流 执行流拥有独立的堆栈/寄存器共享全部的内存&#xff08;指针可以互相引用&#xff09; 3.实现原子性 lock(&lk)unlock(&lk) 实现临界区(critical section)之间…

keepalived脑裂现象

Keepealived最常见的问题是会出现脑裂现象&#xff1a; Master一直发送心跳消息给backup主机&#xff0c;如果中间的链路突然断掉&#xff0c;backup主机将无法收到master主机发送过来的心跳消息&#xff08;也就是vrrp报文&#xff09;&#xff0c;backup这时候会立即抢占mas…