学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁】

=========================================================================

相关代码gitee自取:C语言学习日记: 加油努力 (gitee.com)

=========================================================================

接上期:
学C的第十天(继续深入学习函数、函数递归、练习)-CSDN博客

=========================================================================

                 

函数栈帧的创建和销毁

  • 越高级编译器越不容易学习和观察该过程

                    

  • 同时在不同的编译器下,函数调用过程中栈帧的创建略有差异的,
    具体细节取决于编译器的实现

               

寄存器:ebpesp(和函数栈帧有关)

esp栈顶指针        ;        ebp栈低指针


  • 寄存器集成在CPU上

               

  •  ebpesp 这两个寄存器中存放的是地址

               

  • 这两个地址是用来维护函数栈帧
                        

1. 每一次函数调用,都要在栈区创建一个空间

               

2. 正在调用哪个函数,esp 和 ebp 就在维护哪个函数的函数栈帧

               

3. esp 和 ebp 之间的空间就是系统为这次函数所调用的空间,叫这次函数的函数栈帧

               

4. 栈区的使用习惯使用地址,使用地址

               

5. 空间消耗时,从高地址向低地址消耗

               

6. 再开辟新空间时,使用的空间是上面的空间往上使用

               

7. 像栈一样,放数据在顶上(栈顶)放数据


               

测试代码:

#include <stdio.h>int Add(int x, int y)
{int z = 0;z = x + y;return z;
}int main()
{int a = 10;int b = 20;int c = 0;c = Add(a, b);printf("%d\n", c);return 0;
}
函数栈帧图示:

                      


                    

VS2013中,main函数也是被其它函数调用的

               

mainCRTStartup        -->        __tmainCRTStartup        -->        main函数 

            (调用)                                            (调用)

                    

 实际开辟的空间为:

              

 (查看汇编代码:)

                  


                    

函数栈帧实现过程(重点):

(1).push压栈):给栈顶放一个元素

        [ 补充:pop出栈) -->   从栈顶删除一个元素 ]

(压栈前:)

                  

(压栈后:esp会往上移,移到压的元素上方)

            

            

---------------------------------------------------------------------------------------------

            

            

(2).mov把后面的值赋给前面,把esp的值赋给ebp):

            

            

---------------------------------------------------------------------------------------------

            

            

(3).sub让esp减去一个十六进制数):

            

            

---------------------------------------------------------------------------------------------

            

            

(4).连续push三次:

            

            

---------------------------------------------------------------------------------------------

            

            

(5).leaload effective address -- 加载有效地址,
把一个有效地址加载到edi中
):

            

            

---------------------------------------------------------------------------------------------

            

            

(6).两次mov后,rep stos

之前出现过的“烫烫烫”乱码的原因:

              

变量未初始化,变量里面的数据是“cc cc cc cc”,
这些“cc cc cc cc”在使用后会产生随机值
"烫烫烫",而初始化就会将这些随机值覆盖

            

---------------------------------------------------------------------------------------------

            

            

(7).产生局部变量:int a = 10; (mov)

            

            

---------------------------------------------------------------------------------------------

            

            

(8).产生局部变量:int b = 20; (mov)

            

            

---------------------------------------------------------------------------------------------

            

            

(9).产生局部变量:int c = 0; (mov)

              

             

===================================================================== 

                

(总结上面步骤)局部变量(上面的a、b、c)的创建过程:

                 

  • 为这次函数调用创建函数栈帧   -- (1)~(6)
                     
  • 在函数栈帧中找到空间把局部变量放进去   --(7)~(9)

=====================================================================

                  

(10).调用函数:传参(mov

            

            

---------------------------------------------------------------------------------------------

            

            

(11).调用函数:传参(push

            

            

---------------------------------------------------------------------------------------------

            

            

(12).调用函数:传参(mov

            

            

---------------------------------------------------------------------------------------------

            

            

(13).调用函数:传参(push

            

            

---------------------------------------------------------------------------------------------

            

            

(14).call:调用函数(进入Add()函数)

            

            

---------------------------------------------------------------------------------------------

            

            

(15).进入Add()函数后:

              

当前开辟的空间情况:)

            

            

---------------------------------------------------------------------------------------------

            

            

(16).Add()函数push

            

            

---------------------------------------------------------------------------------------------

            

            

(17).Add()函数mov

            

            

---------------------------------------------------------------------------------------------

            

            

(18).Add()函数sub

            

            

---------------------------------------------------------------------------------------------

            

            

(19).Add()函数连续三次push

            

            

---------------------------------------------------------------------------------------------

            

            

(20).Add()函数lea(加载有效地址) --> mov  --> mov --> rep stos

            

            

---------------------------------------------------------------------------------------------

            

            

(21).Add()函数中产生局部变量:int z = 0; (mov)

            

            

---------------------------------------------------------------------------------------------

            

            

(22).Add()函数中进行计算:z = x + y

                

 形参的产生和使用:
  • 形参是对实参的临时拷贝:形参是调用的main函数中对变量的拷贝,
    即下图
     ecxeax所以改变形参,改变的也只是 ecxeax ,
    并不会改变main函数中的实参

                        
  • 压栈时:先压的b’,所以在a‘下面,所以传参是先传的形参y再传的形参x
                    
  • 形参的使用:通过指针的偏移量找到形参
                     

                

            

            

---------------------------------------------------------------------------------------------

            

            

(23).Add()函数计算后进行返回return z

            

            

---------------------------------------------------------------------------------------------

            

            

(24).Add()函数调用完后销毁空间返回main函数
pop -- 出栈(弹出栈顶元素)

            

            

---------------------------------------------------------------------------------------------

            

            

(25).Add()函数调用完后销毁空间返回main函数:ret -- call函数调用完后
返回main函数call的下一条指令(之前留的地址会出栈)

            

            

---------------------------------------------------------------------------------------------

            

            

(26).main函数:销毁形参

            

            

---------------------------------------------------------------------------------------------

            

            

(27).main函数:使用Add函数的返回值

            

            

---------------------------------------------------------------------------------------------

            

            

(28).最后main函数的销毁和Add()函数的销毁类似

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

会声会影2024旗舰版系统配置要求及格式支持

会声会影2024旗舰版是一款广受欢迎的视频编辑软件&#xff0c;它的最新版本&#xff0c;会声会影2023&#xff0c;已经发布。在这篇文章中&#xff0c;我们将探讨会声会影2024旗舰版系统配置要求及格式支持 会声会影2024是一款专业的视频剪辑软件&#xff0c;能够帮助用户制作高…

java:jpa、Hibernate、Spring Data JPA、ORM以及和mybatis的区别

文章目录 Java连接数据库几种方式JPAHibernate和Spring Data JPAORM框架jpa和mybatis区别Spring Boot JPA使用例子1、创建库和表2、添加依赖3、配置数据源和Hibernate属性4、配置实体类5、创建一个继承JpaRepository的接口&#xff1a;6、创建一个控制器&#xff08;Controller…

java反射和注解3-仿照retrofit组装接口参数

本片文章将用反射和注解仿照retrofit只需要传入一个带有给定注解的接口&#xff0c;通过调用接口就能直接将传入的数据和注解进行结合&#xff0c;生成对应参数 1&#xff0c;自定义注解 对字段的修饰 Retention(RetentionPolicy.RUNTIME) Target(ElementType.PARAMETER) pu…

代码块02使用细节-Java

代码块02使用细节 四、使用细节1、static代码块/静态代码块&#xff0c;随着类的加载而执行&#xff0c;且只执行一次2、 类什么时候被加载 [重要 ! ]案例演示&#xff1a;static代码块 3、普通代码块&#xff0c;在创建对象实例时&#xff0c;会被隐式的调用。案例演示&#x…

【算法每日一练]-图论(保姆级教程篇7 最小生成树 ,并查集模板篇)#村村通 #最小生成树

目录 题目&#xff1a;村村通 并查集 题目&#xff1a;最小生成树 kruskal算法 prim算法 先引入问题&#xff1a; 要在n个城市之间铺设光缆&#xff0c;主要目标是要使这 n 个城市的任意两个之间都可以通信&#xff0c;但铺设光缆的费用很高&#xff0c;且各个城市之间铺…

C++ day37 贪心算法 单调递增的数字 监控二叉树

题目1&#xff1a;738 单调递增的数字 题目链接&#xff1a;单调递增的数字 对题目的理解 返回小于或等于n的最大数字&#xff0c;且数字是单调递增&#xff08;单调递增数字的定义&#xff1a;每个相邻位上的数字满足x<y&#xff09; 贪心算法 注意本题的遍历顺序是从…

羽隔已就之图像处理之BP神经网络入门

小y最近非常忙&#xff0c;这一年来&#xff0c;活很多&#xff0c;一直在加班、出差&#xff0c;也没好好休息过。最近在武汉出差一个多月了&#xff0c;项目逐渐完结&#xff0c;有点闲时间了&#xff0c;回首望&#xff0c;这一年设定的很多目标都没完成。 还记得&#xff0…

关于Unity中字典在Inspector的显示

字典在Inspector的显示 方法一&#xff1a;实现ISerializationCallbackReceiver接口 《unity3D游戏开发第二版》记录 在编辑面板中可以利用序列化监听接口特性对字典进行序列化。 主要继承ISerializationCallbackReceiver接口 实现OnAfterDeserialize() OnBeforeSerialize() …

动态规划--使用最小花费爬楼梯

题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 示例…

【FGPA】Verilog:JK 触发器 | D 触发器 | T 触发器 | D 触发器的实现

0x00 JK 触发器 JK 触发器是 RS 触发器和 T 触发器的组合&#xff0c;有两个输入端 J 和 K&#xff0c;如果两个输入端都等于 1&#xff0c;则将当前值反转。 行为表 状态图 Timing Diagram Circuit JK 触发器的设计目的是防止 RS 触发器在输入 S 和 R 均等于 …

2020年1月31日 Go生态洞察:pkg.go.dev的未来步骤

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

安卓开发学习---kotlin版---笔记(一)

Hello word 前言&#xff1a;上次学习安卓&#xff0c;学了Java开发&#xff0c;简单的搭了几个安卓界面。这次要学习Kotlin语言&#xff0c;然后开发安卓&#xff0c;趁着还年轻&#xff0c;学点新东西&#xff0c;坚持~ 未来的你会感谢现在努力的你~ 主要学习资料&#xff1a…