函数栈帧的创建与销毁

目录

引言

基础知识

内存模型  ​

寄存器的种类与功能

常用的汇编指令

函数栈帧创建与销毁

main()函数栈帧的创建

NO1. 

NO2.

NO3. 

NO4.

NO5. 

NO6.

main()函数栈帧变量的创建

调用Add()函数栈帧的预备工作——传参

NO1.

NO2.

NO3. 

Add()函数栈帧的创建

Add()函数栈帧变量的创建并运算

NO1.

NO2.

NO3. 

NO4. ​

Add()函数栈帧的销毁 ​

NO1. ​

NO2. ​

NO3. ​

返回main()函数栈帧

问题

NO1.

NO2.

NO3.


引言

在前期学习当中,我们可能会有很多困惑?比如

  • 局部变量是怎么创建的?
  • 为什么局部变量的值是随机?
  • 函数是怎么传参的?传参的顺序是怎样的?
  • 形参和实参是什么关系?
  • 函数调用是怎么做的?
  • 函数调用是结束后怎么返回的?

建议大家在观察函数栈帧创建与销毁时,使用的环境不需要太高级的编译器,越高级的编译器,越不容易学习和观察。同时在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,具体细节取决于编译器的实现。 

基础知识

电脑中的任何指令都在CPU上运行,但CPU只负责运算不负责存储。

数据都存储在寄存器缓存内存中。

想了解函数栈帧的创建和销毁我们就需要了解到:内存模型,寄存器常用汇编指令。

那关于寄存器和缓存等之间的关系,会在后面的博文讲解到。 

内存模型  

 这只是一个大致的介绍,后面博文我们也会详细去介绍到内存模型。

寄存器的种类与功能

 在我们的函数栈帧创建与销毁。我们重点使用到ESP和EBP。

  • ESP(esp):栈指针寄存器(extended stack pointer)栈顶指针,堆栈的顶部是地址小的区域,压入堆栈的数据越多,esp也就越来越小。在32位平台上,esp每次减少4个字节。其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。是CPU机制决定的,push,pop等指令会自动调整esp的值。
  • EBP(ebp):基址指针,指栈的栈底指针基址指针寄存器(extended base pointer)。一般与esp配合使用,可以存取某时刻的esp,这个时候就是进入一个函数内后,CPU会将esp的值赋给ebp,此时刻就可以通过ebp对栈进行操作。比如获取函数参数,局部变量等。其内存放一个指针,该指针永远指向系统栈最上面一个栈帧的底部。

  • espebp这两个寄存器放置的是地址,这两个地址是用来维护函数栈帧的。每个函数调用,都需要在栈区创建一个空间。当正在调用某函数时,espebp就维护这个函数栈帧的空间。
  • 栈区使用:从高地址向低地址消耗/使用

常用的汇编指令

这里我们只讲解几个我们函数栈帧创建等的汇编指令

函数栈帧创建与销毁

了解了上面的基础知识,我们先大致来看下函数栈帧怎样创建与销毁 。

首先我们有想过一个问题吗?就是main()函数也是函数,那也是被哪个函数调用的吗?

当然。在VS2013中,main()函数也是被其他函数调用的

接下来我们进入正题,函数栈帧的创建与销毁。示例代码:

//函数栈帧创建与销毁
#include<stdio.h>
int Add(int x, int y)
{int z = 0;z = x + y;return z;
}
int main()
{int a = 20;int b = 10;int c = 0;c= Add(a, b);printf("%d\n", c);return 0;
}
//为了细致全方面去观察函数栈帧的创建与销毁,所以把代码拆分的很细
//F10调试-----→转到反汇编
//转到汇编语言去观察时记得把符号名去掉,更易观察

main()函数栈帧的创建

NO1. 

首先我们知道关于此时此时栈区_tmainCRTStartup()函数被调用了,接下来它需要调用main() 

 PUSH 把字压入堆栈。

 先将ebp的值这个空间大小放置到栈区新开辟的栈帧中。

 再将esp向上移动到ebp的栈顶的位置。

(esp的值减少4个字节,值减少了ebp这么多的空间大小)

首先esp的值减少4个字节,再将ebp的值压入栈中。

 NO2.

 MOV 传送字或字节。

 将esp的值赋给ebp。这里并不是将esp所指向内存空间的值赋给ebp

NO3. 

 SUB 减法.

 将esp-0E4H(228)即将esp指针向低地址方向移动0E4H字节。

NO4.

此刻我们发现mian()函数有自己栈区所欲开辟的新空间,那接下来? 

  •  首先将esp的值减少4个字节,再将ebx的值压入栈中。
  •  首先将esp的值减少4个字节,再将esi的值压入栈中。
  •  首先将esp的值减少4个字节,再将edi的值压入栈中。

(不确定顺序先后?)

NO5. 

LEA(lea):load加载。 load effective address

[ebp-0E4h]这么多的空间大小放到edi里面去。

NO6.

以上四段汇编代码的意思是:

edi这个位置向下的39h 这么多的空间大小的双字节dword(4个字节)全部放置为0CCCCCCCCh 这样的内容。

每一次初始化dword,共初始化19h次。

main()函数栈帧变量的创建

  MOV 传送字或字节。

  把0AH(10)的值赋给 地址为[ebp-8] 的双字节空间

  把14h(20)的值赋给 地址为[ebp-14h] 的双字节空间

  把0的值赋给 地址为[ebp-20h] 的双字节空间  

那如果没有初始化呢?

 那么abc的位置就会被初始化为CCCCCCCC随机值。打印abc的时候也就是随机值。

调用Add()函数栈帧的预备工作——传参

NO1.

MOV PUSH 

  • 将地址为[ebp-14h] 双字节空间大小 的值赋给eax
  • esp的值减少4个字节,将eax压栈到栈中

NO2.

 MOV PUSH

  • 将地址为[ebp-8]双字节空间大小 的值赋给ecx
  • esp的值减少4个字节,将ecx压栈到栈中

NO3. 

CALL

  • esp的值减少4个字节,再将下一条指令的IP(00921A30)压入栈中。
  • F11之后,移动到调用的Add()函数的子程序里。

Add()函数栈帧的创建

现在我们正式进入Add函数。首先和main()函数栈帧一样,我们需要在栈区开辟一块新的空间。因为在前面我们详细的讲解了main()函数栈帧的创建,这里大家可以先自己动小脑瓜子想想,画一画过程图,再看最后结果。 

  • 将ebp的值压入栈中,esp减少4个字节。
  • 将esp的值赋给ebp,这里并不是将esp所指向的内存空间的值赋给ebp。
  • 将esp-0CCh,即esp向上移动0CCh的空间大小。
  • 将ebx压入栈中,esp的值减少4个字节。
  • 将esi压入栈中,esp的值减少4个字节。
  • 将edi压入栈中,esp的值减少4个字节。
  • 从edi向下33h的双字节空间大小全部初始化为0CCCCCCCCh,每一次初始化dword双字节大小,共初始化33h次。

Add()函数栈帧变量的创建并运算

NO1.

 MOV

 将0的值赋给内存地址为[ebp-8]的双字节空间。

  

NO2.

MOV

将内存地址[ebp+8] 的双字节空间数据内容 赋给eax

ADD

将内存地址[ebp+0Ch] 的双字节空间数据内容 加上eax的值 再赋给eax

NO3. 

 MOV

eax寄存器道德数据内容 赋给内存地址为[ebp-8]的双字节空间。

NO4. 

MOV

将内存地址为[ebp-8]的双字节空间大小中的数据,赋给eax

Add()函数栈帧的销毁 

NO1. 

 POP

  • 先将esp所指的地址处的值赋给edi,esp值增加4个字节。
  • 先将esp所指的地址处的值赋给esi,esp值增加4个字节。
  • 先将esp所指的地址处的值赋给ebx,esp值增加4个字节。

NO2. 

MOV

ebp的值赋给esp,这里并不是将ebp所指向的内存空间的值赋给esp

POP

ebp弹回到原来main()函数栈帧的栈底位置,esp增加4个字节(esp来到ebp的栈顶位置)

NO3. 

RET

执行完这条命令,就自动返回刚才call指令的下一条。

返回main()函数栈帧

 之后的main()函数栈帧的销毁和Add()函数栈帧的销毁同理,所以我们就不再讲解了。 

问题

NO1.

为什么将call指令的下一条指令的地址压入栈帧中?

 确保我们调用完Add()函数后,返回main()函数栈帧时能回到call函数的下一条指令执行。

NO2.

为什么将main()函数栈帧的栈底地址ebp压入栈顶? 

为了当函数调用返回时,esp和ebp都回到原来维护main()函数栈帧的位置。 

NO3.

为什么说形参是实参的一份临时拷贝?

还没有调用Add()函数的时候,已经将参数ab传递过去了,在函数栈帧中已经为ab创建了一块空间,在使用xy的时候,返回这里使用即可。所以我们并没有在Add()函数中为xy创建空间。 

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎指正!

接下来的博文会更新一些练习题,到实践中去加深对知识的理解。🙂🙂🙂

代码----------→【gitee:https://gitee.com/TSQXG】

联系----------→ 【邮箱:2784139418@qq.com】

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

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

相关文章

机器学习:什么是分类/回归/聚类/降维/决策

目录 学习模式分为三大类&#xff1a;监督&#xff0c;无监督&#xff0c;强化学习 监督学习基本问题 分类问题 回归问题 无监督学习基本问题 聚类问题 降维问题 强化学习基本问题 决策问题 如何选择合适的算法 我们将涵盖目前「五大」最常见机器学习任务&#xff1a…

Python Opencv实践 - 图像中值滤波

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) print(img.shape) pixel_count img.shape[0] * img.shape[1] print(pixel_count)#为图像添加椒盐噪声 #参考资料&#xf…

如何通过振动判断设备健康度?以PreMaint设备数字化平台为例

在工业生产过程中&#xff0c;设备的健康状况直接关系到生产效率和安全。而振动分析作为一种重要的设备健康监测手段&#xff0c;可以通过监测设备的振动情况来判断其健康状况。本文将以PreMaint设备数字化平台为例&#xff0c;探讨如何通过振动分析来判断设备的健康度&#xf…

电商系统架构设计系列(九):如何规划和设计分库分表?

上篇文章中&#xff0c;我给你留了一个思考题&#xff1a;分库分表该如何设计&#xff1f; 今天这篇文章&#xff0c;我们来聊一下如何规划和设计分库分表&#xff0c;以及要考虑哪些问题。 引言 当要解决海量数据的问题&#xff0c;就必须要用到分布式的存储集群了&#xff…

ROS2 中的分布式系统

一、说明 当您运行 ROS2 应用程序时&#xff0c;通常需要在不同机器的不同位置运行 ROS2 节点。由于 ROS2 在抽象的 DDS 层中使用节点之间的通信&#xff0c;因此我们可以非常轻松地安排通信。 为了充分理解 ROS2 的架构&#xff0c;我建议您熟悉本文。 出于本文的目的&#xf…

江西南昌电气机械三维测量仪机械零件3d扫描-CASAIM中科广电

精密机械零部件是指机械设备中起到特定功能的零件&#xff0c;其制造精度要求非常高。这些零部件通常由金属、塑料或陶瓷等材料制成&#xff0c;常见的精密机械零部件包括齿轮、轴承、螺丝、活塞、阀门等。精密机械零部件的制造需要高精度的加工设备和工艺&#xff0c;以确保其…

微信小程序使用npm引入三方包详解

目录 1 前言2 微信小程序npm环境搭建2.1 创建package.json文件2.2 修改 project.config.json2.3 修改project.private.config.json配置2.4 构建 npm 包2.5 typescript 支持2.6 安装组件2.7 引入使用组件 1 前言 从小程序基础库版本 2.2.1 或以上、及开发者工具 1.02.1808300 或…

Mac常见恶意软件再现,办公应用程序潜藏风险如何防范?

Mac电脑正受到臭名昭著的XLoader恶意软件的新变种的攻击&#xff0c;该恶意软件已被重写为在最好的MacBook上本地运行。 虽然XLoader至少从2015年开始出现&#xff0c;但在2021年发现macOS变体之前&#xff0c;它主要用于针对Windows PC。然而&#xff0c;该版本是作为Java程序…

【Redis从头学-7】Redis中的Set数据类型实战场景之用户画像去重、共同关注、专属粉丝

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 &#x1f4d6;所属专栏&#xff1a;Re…

【leetcode 力扣刷题】移除链表元素 多种解法

移除链表元素的多种解法 203. 移除链表元素解法①&#xff1a;头节点单独判断解法②&#xff1a;虚拟头节点解法③&#xff1a;递归 203. 移除链表元素 题目链接&#xff1a;203.移除链表元素 题目内容&#xff1a; 理解题意&#xff1a;就是单纯的删除链表中所有值等于给定的…

0基础学习VR全景平台篇 第88篇:智慧眼-成员管理

一、功能说明 成员管理&#xff0c;是指管理智慧眼项目的成员&#xff0c;拥有相关权限的人可以进行添加成员、分配成员角色、设置成员分类、修改成员以及删除成员五项操作。但是仅限于管理自己的下级成员&#xff0c;上级成员无权管理。 二、前台操作页面 登录智慧眼后台操…

公文校对的艺术:如何确保你的正式文件零错误?

公文是政府和企业中最重要的正式文件之一。一个小小的错误&#xff0c;不仅会影响公文的专业性&#xff0c;甚至可能带来法律和经济后果。因此&#xff0c;如何进行精准的公文校对成为了一门必不可少的技能。接下来&#xff0c;我们将分享一些专业的公文校对技巧&#xff0c;并…

Docker容器与虚拟化技术:容器运行时说明与比较

目录 一、理论 1.容器运行时 2.容器运行时接口 3.容器运行时层级 4.容器运行时比较 5.强隔离容器 二、问题 1.K8S为何难以实现真正的多租户 三、总结 一、理论 1.容器运行时 &#xff08;1&#xff09;概念 Container Runtime 是运行于 k8s 集群每个节点中&#xff…

greenplum gpfdist外部表(只读)的实现---理论

经过项目的验证测试以及初步商用化&#xff0c;本篇将进一步讲解greenplum外部表的实现原理&#xff0c;包括设计原则、交互协议与实现流程。gpfdist工具的简介与使用见回顾greenplum gpfdist工具。 1 设计原则 greenplum作为分布式分析型数据库&#xff0c;其每个节点都是独立…

如何批量加密PDF文件并设置不同密码 - 批量PDF加密工具使用教程

如果你正在寻找一种方法来批量加密和保护你的PDF文件&#xff0c;批量PDF加密工具是一个不错的选择。 它是一个体积小巧但功能强大的Windows工具软件&#xff0c;能够批量给多个PDF文件加密和限制&#xff0c;包括设置打印限制、禁止文字复制&#xff0c;并增加独立的打开密码。…

【Django】Task4 序列化及其高级使用、ModelViewSet

【Django】Task4 序列化及其高级使用、ModelViewSet Task4主要了解序列化及掌握其高级使用&#xff0c;了解ModelViewSet的作用&#xff0c;ModelViewSet 是 Django REST framework&#xff08;DRF&#xff09;中的一个视图集类&#xff0c;用于快速创建处理模型数据的 API 视…

C++ 网络编程项目fastDFS分布式文件系统(四)-fastCGI项目相关技术以及linux搜狗输入法相关问题。

目录 1. Nginx作为web服务器处理请求 2. http协议复习 Get方式提交数据 Post方式提交数据 3. fastCGI 3.1 CGI 3.2 fastCGI 3.3 fastCGI和spawn-fcgi安装 1. 安装fastCGI 2. 安装spawn-fcgi 3.4 nginx && fastcgi 4其他知识点 1. fastCGI环境变量 - fas…

滑动验证码-elementui实现

使用elementui框架实现 html代码 <div class"button-center"><el-popoverplacement"top":width"imgWidth"title"安全验证"trigger"manual"v-model"popoverVisible"hide"popoverHide"show&quo…

P1328 [NOIP2014 提高组] 生活大爆炸版石头剪刀布

题目描述 石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样&#xff0c;则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。 升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势: 斯波克:《星际迷航》主…

FairyGUI编辑器自定义菜单扩展插件

本文涉及到的软件有&#xff1a;FairyGUI&#xff0c;VSCode 代码环境涉及到了&#xff1a;Lua VSCode插件&#xff1a;EmmyLua 在编写FairyGUI编辑器菜单前&#xff0c;了解一下FairyGUIEditor的API会有效的帮助我们解决很多问题。FairyGUI的扩展是通过编辑器自带的插件功能…