当前位置: 首页 > news >正文

杭州建网站哪家口碑好rap做词网站

杭州建网站哪家口碑好,rap做词网站,手机网页制作软件,企业网站开发建设目录 前言 补充知识 一、函数线帧是什么? 二、函数线帧的实现(举例说明) 两数之和代码 ​编辑两数之和 汇编代码分析 执行第一条语句 执行第二条语句 执行第三条语句 执行第四、五、六条语句 执行第七条语句 执行第八、九、十条语句 执行第十…

目录

前言

补充知识

一、函数线帧是什么?

二、函数线帧的实现(举例说明)

两数之和代码

​编辑两数之和 汇编代码分析

 执行第一条语句

执行第二条语句

执行第三条语句

执行第四、五、六条语句

执行第七条语句

执行第八、九、十条语句

执行第十三条语句

执行第十四、十五条语句

执行第十六条语句

执行第十七条语句

 执行第十七、十八条语句

 执行第十九、二十条语句

进入Add函数里面 

Add函数预开辟空间

创建z

 相加

实际传参

返回z

弹出

回收没用空间,回到main函数

将数放进c中

三、总结

局部变量是怎么创建的,局部变量的创建?

 为什么局部变量的值是随机值?

函数是怎么传参的?传参的顺序是什么样的?

形参和实参是什么关系?

函数调用结果是怎么返回的?


前言

这里补充一下函数线帧的创建和销毁,我们知道函数调用一次就会占用一次栈内存。每一次函数调用都会为本次函数调用分配内存空间(是在内存的栈区),为本次函数调用分配的内存空间叫做被称为这次函数调用的栈帧空间,函数栈帧的创建和销毁。

编译器越高级,那么 就越不容易发现在函数调用的过程中线帧的创建,具体细节取决于编译器的实现。新的编译器由于考虑各种各样的问题,所以封装的更加复杂,不容易分离出来函数栈帧创建的过程。

栈空间的使用是从高地址向地地址增长。

寄存器是集成到cpu上的,跟mian函数是没有关系的,是独立的,硬盘,内存,寄存器是相互独立的。


补充知识

我们知道计算机中有寄存器,而寄存器包括很多,例如eax,ebx,ecx,edx,ebp,esp等。

而ebp,esp这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。

每一个函数调用,都要在栈区上创建一个空间,而当调用哪个函数,esp和ebp就会维护哪个函数线帧。通常esp称为栈顶指针,sbp称为栈底指针。

当我们在调用main函数之前,会调用_tmainCRTStartup这个函数,这个函数内部调用了main函数,而这个函数又是被mainCRTStartup这个函数调用的。

其实在VS2013中,main函数也是被其他函数调用的。

一、函数线帧是什么?

函数栈帧是指在函数调用过程中,为了保存函数的局部变量、参数和执行上下文等信息而创建的一块存储区。

每次函数调用时,都会在栈上创建一个新的栈帧,用于存储该函数的局部变量、参数和执行上下文等信息。栈帧通常包括以下几个部分:

  1. 局部变量区域:用于存储函数内部定义的局部变量和临时变量等。

  2. 参数区域:用于存储函数的参数值。

  3. 返回地址:用于保存函数调用完成后的返回地址,以便能够返回到调用方继续执行。

  4. 上一级函数的栈帧指针:用于保存上一级函数的栈帧地址,以便能够返回到上一级函数。

  5. 其他上下文信息:如调用方的寄存器值、异常处理信息等。

函数栈帧的创建和销毁是由编译器和操作系统自动完成的,开发者一般无需手动管理。函数栈帧的创建和销毁按照函数调用的顺序,形成一个栈结构,因此也被称为调用栈或执行栈。在函数调用完成后,栈帧被销毁,栈指针回退到上一级函数的栈帧,继续执行上一级函数。这种递归结构的栈帧可以保证函数调用的嵌套和返回顺序的正确性。

二、函数线帧的实现(举例说明)

两数之和代码

#define _CRT_SECURE_NO_WARNINGS
#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;
}

我们通过用vs2022来进行讲解,例子用的上述代码+两数之和函数。

两数之和 汇编代码分析

我们通过F10来调试代码,之后在代码界面点击右键,选择反汇编,就会出现一个新页面,而这个页面就是图中右侧的,这是C语言对应的汇编代码。

把显示符号取消勾选,因为选上会显示符号名,而我们想要看其地址和布局,所以就去掉。

这时候里面就写的是符号的地址了,而不是符号名字了。

因为main函数是其它函数调用的,所以调用的__tmainCRTStartup已经分配好栈空间了,此时esp和ebp就会维护这个函数线帧(前提下面是高地址,上面是高地址):

 执行第一条语句

意思是将ebp进行压栈,所以栈顶就多了一个元素(ebp),因为esp维护的是栈顶,所以esp指向往上移动了一步。内存就压进去一个ebp。

执行第二条语句

mov的意思就是把后者的值赋给前者,这里也就也是将esp的值赋给ebp,我们知道esp的值赋给了ebp,那么ebp的地址就指向esp,而esp又指向栈顶,ebp就指向了之前esp指的地方,所以就变成了下面:

执行第三条语句

sub是前者减去后者,也就是esp减去后面的数字,0E4h是八进制,esp减去这个数后值变小了,所以esp就指向了上面的某一位置,我们知道esp和ebp之间的就是函数的栈帧空间,所以他们俩之间的就是开辟出来main函数的预留空间,也就是main函数的栈帧。

执行第四、五、六条语句

分别压栈压进去三个元素,ebx,esi,edi,同时esp的值会往上走。每一个push,esp都会往上挪一下。

执行第七条语句

lea实际上是 load effecitive address(加载有效地址)的意思,这里面就是将后面的有效地址加载到edi中,相当于edi里面加上了一个地址。它实际上就似乎找了第三条语句的地址。

执行第八、九、十条语句

 这三步真正有意义的是第三步,前两步是把后面的值放到前面的里面。而第三步则是从edi开始,将39h这么多个空间全部改成eax的内容,每一次初始化(dword个字节,一个4个字节)。实际就是将main函数预开辟的空间的全部数据改成0ccccccch。

自此,main函数的栈帧就已经准备好了。接下来才是操作的代码。

执行第十三条语句

这里就是将0Ah(10) 放到ebp-8这个地址的地方,也就是a的地方,这块地方就放了10

执行第十四、十五条语句

与第十三条语句一样差不多,都是赋值给对应地址。

这就可以总结出函数创建的时候局部变量的创建规律:

首先,创建这个函数的函数栈帧

之后找到一些空间把变量放进去

 接下来就是调用函数,因为函数调用需要传参

执行第十六条语句

从这里我们可以看见后面这个地址与前面的有一个地址一样,这里就是b的地址,所以这里把b的值也就是20放进了eax.

执行第十七条语句

这里进行压栈,把eax压栈,放到栈顶。如下图:

 执行第十七、十八条语句

这里与前两句一样,先把a的值存到ecx中,之后在把ecx进行压栈。 

 执行第十九、二十条语句

执行call指令,会调用一个地址,并且把地址压入栈中,而压入栈中的地址就是下一条指令的地址,也就是add的地址00521918(每次编译可能会不同)。

为什么要调用下一条指令的地址,因为call指令会进入add函数,但进入函数后还需要回来,回来怎么回来,就需要一个地址来进行返回,来回到call指令的下一条指令,再从这个地址往下执行命令。

进入Add函数里面 

Add函数预开辟空间

再往下运行,就进入到了Add函数里面,而上面这些就是为了为Add函数预开辟线帧空间。

与之前开辟main函数的线帧空间一样。开辟之后就如上图所示。

创建z

在ebp-8的地址传入z,这里面放的是0

 相加

我们知道ebp -8就是ecx的位置,ebp-12就是eax的位置,同时ecx里面是a的值,eax里面是b的值,所以给他们给他们命名为是a'和b'。

这里面吧ebp+8的值(10)加到eax里面去,所以eax就为10,之后吧ebp+0ch(12)的值(20)再加入eax中,这时候eax里面就为30,加起来之后再把eax的值放进ebp-8的地址里面去,而z恰好是ebp-8的地址,所以z就为30。

 这里我们发现函数参数x,y并没有,而是通过调用指令进行传参,将形参进行push压栈,压到某一位置,参数是从右向左传的,当真的来到函数内部来调用这两个数相加的时候会发现,形参根本不是在函数内部创建的,而是回来找刚才调入进来传参传入的这个空间,压进去这个空间,上图a'就认为是x,b'就认为是y,也就是x+y,之后传入z的空间。

实际传参

实际传参是还没有调用这个函数的时候,参数a和b就已经传过去了,在函数栈帧中压入了两个参数(b和a),压进去之后,真正用函数内部的时候,其实是再找回之前压入栈中的这两个值,然后相加之后再给z。所以形参是实参的临时拷贝这句话得到了验证。

返回z

因为我们知道函数调用完后会销毁,同时z也会销毁,所以把ebp-8里的值也就是z的值30放进eax寄存器中,寄存器不会因为函数的销毁而销毁,所以放在这里就安全了,先保存起来。

弹出

pop就是弹出的意思,弹出一个esp就往下移动一个地址,之后就如下图了:

这时候发现有些空间没有用到,所以应该回收。

回收没用空间,回到main函数

这里将ebp的位置传入esp,用pop(把栈顶的元素 弹出来放到ebp里面去),因为此时ebp(main地址)存的是之前下面的ebp地址,所以ebp又回到了下面(之前的地方),而esp则指向下call指向下一条指令的地址,因为之前的ebp已经弹出了。

这样就回到了main函数里面,这样栈帧空间又是又esp和ebp开始维护。

现在栈顶元素是call指令下一条指令的地址

这条指令就是回到了call指向下一条指令的地址。再往下执行就是:

esp+8之后,esp就指向了如下图:

当指向这里的时候,就说明把ecx和eax就还给了操作系统。所以这时候形参的空间就还给了操作系统,释放了。

将数放进c中

 这里将eax(计算的和)放进ebp-20h(c)里面去,这样返回值就带了回来。

至此,函数线帧的创建和销毁就结束了。

总图:

三、总结

局部变量是怎么创建的,局部变量的创建?

首先先分配好栈帧空间,然后在这个空间里面分配一些空间来创建局部变量

 为什么局部变量的值是随机值?

因为这个随机值是我们自己放进去的,初始化后就把随机值覆盖了,这时候就不是随机值了

函数是怎么传参的?传参的顺序是什么样的?

当我们要调用这个函数的时候,其实函数还没有调用的时候,就已经把两个参数从右向左push压栈压进去了,当真正进入函数的时候,通过指针的偏移量来找到之前传入的这两个形参参数

形参和实参是什么关系?

形参是实参的一份临时拷贝,形参确实是在压栈的时候开辟的空间,它和实参只是值是相同的、空间是独立的,改变形参是不会影响实参的。

函数调用结果是怎么返回的?

之前就把call指令的下一个指令的地址就压进栈了,把ebp调用这个函数的上一个函数栈帧的ebp就存进去了,当我们函数调用完要返回时候,弹出edp,就可以找到原始(上一个函数调用的ebp),指针往下走就可以找到esp的地址,回到了mian函数。

记住call指令的下一个指令地址就可以回来进行下一个命令。

返回值通过寄存器来临时保存,之后进行返回。

http://www.yayakq.cn/news/46594/

相关文章:

  • 运动服装商城网站建设什么网站广告最多
  • 全网营销网站怎么做wordpress视频代码
  • 博州建设局网站做基因表达热图的网站
  • 网站开发公司会计处理design中国官网
  • 做培训的都上哪些网站做医院网站
  • 登封免费网站建设二级建造师证件查询全国联网
  • 网站做引流贵州做网站怎么推广
  • 企业网站栏目设置说明wordpress安
  • 北京网站建设公司公司上海建筑工程有限公司有哪些
  • 电子商务网站设计原理实践报告织梦系统 子网站
  • 富民网站建设学院网站的系统建设方式
  • 网站建站的作用手机上如何制作自己的网站
  • 酒水招商网站大全做和别人一样的网站
  • 做网站必须知道的问题天津差旅管家商旅服务有限公司
  • 微信上做网站怎么做商城网站建设制作设计
  • 机械类网站如何做网站优化桂林两江四湖属于哪个区
  • 网站建设考评表打通WordPress和微信公众号
  • 成都网站建设 工作室无锡赛孚建设工程有限公司网站
  • 雅安市住房和城乡建设局网站怎么修改网站关键词
  • 辽阳网站开发公司上海企业建站咨询
  • 刷q币网站建设百度问答一天能赚100块吗
  • 做老师好还是网站编辑好优惠券网站开发哪家好
  • 杭州企业网站定制增加网站点击量
  • 用哪个网站做相册视频文件夹品牌推广网站策划设计
  • 一个网站是怎么建立的男女做羞羞羞的事视频网站
  • 上海网站建设公司排行可视化网站后台
  • 昆明哪些做网站建设的公司如何管理网站后台
  • 宜春网站建设公司联系方式网站建设课程设计心得体会
  • 六安建设厅网站定制网站建设公司排行
  • html5个人网站源码o2o网站制作公司