函数栈帧的创建和销毁 - 局部变量|函数传参|函数调用|函数返回|图文详解

目录

1.寄存器EBP和ESP

2.函数栈帧的创建

3.函数的调用

4. 函数栈帧的销毁


函数栈帧(function stack frame)是在函数调用期间在上分配的内存区域,用于存储函数的局部变量、参数、以及用于函数调用和返回的相关信息。每当函数被调用时,都会创建一个新的栈帧,函数执行结束后,该栈帧会被销毁。

1.寄存器EBP和ESP

寄存器是位于CPU内部的一组用于存储和处理数据的小型临时存储器。它们被设计用于执行指令、进行算术和逻辑运算、控制程序流程等任务。寄存器通常比内存访问速度更快,因为它们直接集成在CPU内部,而不需要通过外部总线进行访问。

EBP和ESP是 x86 架构下的寄存器,用于在函数调用过程中维护被调用的函数的栈帧。

EBP是扩展基址寄存器(栈底指针),通常用来指向当前函数的栈帧的基址(高地址处)。ESP是栈指针寄存器(栈顶指针),指向当前栈顶的位置(低地址处)

每当函数调用时时,都要在栈区上创建一个空间,并且将栈区的地址分别交由寄存器EBP和ESP来来维护。正在调用的是哪个函数,这两个寄存器就维护哪个函数的栈帧。

2.函数栈帧的创建

接下来通过一个简单求和函数来了解函数栈帧的创建过程

#include<stdio.h>

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int a = 2;
	int b = 1;

	int c = Add(a, b);

	return 0;
}

再来看这段反汇编代码:

可以看到,在进入main函数的时候有一系列的反汇编指令,它们是什么意思?干了什么事情?

下面是一个内存演示图 :在进入main函数之前,ebp和esp分别指向调用main函数的函数的栈底和栈顶。


push ebp(压栈):执行这一条语句后,将ebp的值放在esp的位置,esp维护栈顶位置,所以esp向高地址走一步(值减小)


mov ebp,esp:将esp的值给ebp 


sub esp,0E4h:esp向上低地址处移动,现在ebp与esp之间的空间就是为main函数开辟的栈帧空间


push  ebx , push esi, push sdi:把ebx,esi,edi分别压入栈


lea  edi,[ebp-24h]:ebp的值减去24(十六进制)的偏移量,然后将结果储存在edi中。这样做是为了将edi指向要填充的内存区域的起始地址

mov ecx,9:存储到ecx中。这个值表示要填充的内存区域的大小,单位为dword(Double Word 即4字节).

mov eax,0cccccccch:将0xcccccccc 存储到eax中.

rep stos dword ptr es:[edi]  :一个重复(rep)操作,它会将eax中的值(0xcccccccc)存储到edi所指向的内存地址处,存储的长度为ecx中的值(9 dword)


3.函数的调用

到这里为止,调用main函数的准备工作才算结束,接下来才刚开始执行我们写的代码:

 mov  eax,dword ptr [a]:写入数据到a的位置。这里的b其实就是ebp - 8 的位置

后面的mov都是写入数据,只是写入的位置逐渐向低地址处移动。


终于来到调用函数Add的部分,首先我们进行函数的参数传递

mov eax,dword ptr [b]:将ptr[b]放到eax里去

push eaxeax入栈

mov eax,dword ptr [b]:将ptr[a]放到eax里去

push eaxeax入栈

注意,由上面的四条指令可以看出:虽然形参的顺序是先a后b,但是实际压入栈的顺序是先b后a

call  _Add (03410B9h) :调用函数Add,其中03410B9h是函数的地址。并且把call指令的下一条指令压入栈,使Add函数执行完后知道下一条该执行的指令。


现在来到Add函数里面:

可以发现前面部分跟在调用main函数的时候是相似的,即为Add函数创建栈并初始化。

要注意的时,现在的ebp,esp已经由维护main函数的栈帧变为维护Add函数,因为此时我们已经开始创建Add的栈帧了。

mov eax,dword ptr[x]:这里其实就是找到刚刚压入Add函数的值,即ptr[x]位置的eax,值为1

add eax,dword ptr[y]:将ptr[x]位置的eax,值为2,和prt[x]相加,得到3

mov dword ptr[z],eax把3放入eax,即得到z=3

从这里可以看出,当我们真正进入函数调用两个数相加时,形参根本不是在Add中创建的,而是在Add中找到刚刚调用函数时压入的空间所存放的数据,即图中所示的空间:

所以,这样就能很明确的知道:形参是实参的一份临时拷贝 

mov  eax,dword ptr [z]:将z的值放入eax中,因为z会随着函数的结束而被销毁,要想返回一个值需要用eax这样一个寄存器来保留,因为寄存器是不会随着函数的结束而被销毁的。


4. 函数栈帧的销毁

pop edi、pop esi、pop ebx:弹出edi,,esi,,ebx,同时每次弹出时esp也向高地址处移动

mov esp,ebp:将esp指向ebp的位置

pop ebp:弹出ebp,此时ebp回到main函数中ebp原本的位置,esp由于这一次pop也向高地址处移动1偏移量,指向刚刚保存call的下一条指令的位置,准备执行下一条指令。1

add esp,8:将esp向高地址处移动两个偏移量,此时用于保存之前压入Add函数的两个形参的eax就被释放了

mov dword ptr [c],eax:把刚刚保留c的那个eax复制给c


到这里整个过程就介绍的差不多了,从中我们可以的出许多结论比如:

1.释放栈帧所占用的内存空间,是通过移动栈帧指针,从而允许后续操作直接覆盖数据来实现的

2.函数调用后还能找到下一条执行的语句是因为在调用函数之前,当前函数的上下文需要被保存到栈中,以便在函数执行完毕后能够正确返回到调用函数。

3.函数传参时是将函数调用时传递的参数复制到栈帧中的相应位置,以便函数内部能够访问这些参数。

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

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

相关文章

品牌方年度抖音店铺打造流量运营孵化方案

【干货资料持续更新&#xff0c;以防走丢】 品牌方年度抖音店铺打造流量运营孵化方案 部分资料预览 资料部分是网络整理&#xff0c;仅供学习参考。 PDF共120页&#xff08;完整资料包含以下内容&#xff09; 目录 抖音年度短视频直播运营规划方案 1. 帐号视频发布规划 问…

C++进阶:二叉搜索树介绍、模拟实现(递归迭代两版本)及其应用

上次介绍完多态后&#xff1a;C进阶&#xff1a;详解多态&#xff08;多态、虚函数、抽象类以及虚函数原理详解&#xff09; 也是要开始继续学习了 文章目录 1.二叉搜索树1.1概念1.2二叉搜索树特性1.3 二叉搜索树的操作 2.模拟实现2.1项目文件规划2.2基本结构2.3各种接口、功能…

【数值模型系列】模拟区域网格设置工具WRFDomainWizard网页版使用介绍

大气数值模型首先需要进行模拟区域参数设置&#xff0c;这一过程可以使用WRF官网提供的WRFDomainWizard软件工具&#xff0c;也可以使用QGIS/GIS4WRF&#xff0c;甚至可以手动设置多次调整&#xff08;不推荐但不少人使用&#xff09;。本文介绍WRFDomainWizard工具的网页版&am…

六、循环结构

在python当中有两类循环结构&#xff1a;for循环和while循环 一、遍历for循环 for循环首先判断遍历对象中是否有元素&#xff0c;在依次遍历 for循环常与range&#xff08;&#xff09;函数使用 for i in range(1,10,):#range()函数依次遍历1~10但不包括10print(i,end ) p…

《尚品甄选》:后台系统——通过面向切面编程AOP,实现记录日志功能

文章目录 一、记录日志的意义二、日志数据表结构三、记录日志思想四、切面类环境搭建五、保存日志数据 一、记录日志的意义 后台管理系统记录操作日志的意义非常重要&#xff0c;主要体现在以下几个方面&#xff1a; 安全性&#xff1a;操作日志可以记录管理员操作行为&#…

【每日一题】数组元素的最小非零乘积

文章目录 Tag题目来源解题思路方法一&#xff1a;贪心 写在最后 Tag 【贪心】【快速幂】【数组】【2024-03-20】 题目来源 1969. 数组元素的最小非零乘积 解题思路 方法一&#xff1a;贪心 前言 我们首先来思考一个简单的问题&#xff1a;假设给定三个整数 a a a&#xf…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法&#xff0c;它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试&#xff1a; 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中&#xff0c;系统会暴露于一定数量的用户负载下&…

CodeWhisperer插件

一、前言 产品官网地址&#xff1a;What is CodeWhisperer? - CodeWhisperer Amazon CodeWhisperer 是一个通用的、由机器学习驱动的代码生成器&#xff0c;可实时为您提供代码建议。在您编写代码时&#xff0c;CodeWhisperer 会根据您现有的代码和注释自动生成建议。您的个…

一招让你的Mac重获新生,CleanMyMac助你轻松清理无用垃圾!

一招让你的Mac重获新生&#xff0c;CleanMyMac助你轻松清理无用垃圾&#xff01; 告别卡顿&#xff0c;让你的Mac跑得更快更稳&#xff01; 在当今这个快节奏的生活中&#xff0c;我们的工作和生活早已离不开电脑。特别是对于Mac用户来说&#xff0c;一台轻巧、快捷、稳定的Mac…

代码随想录算法训练营Day51 ||leetCode 309.最佳买卖股票时机含冷冻期 || 714.买卖股票的最佳时机含手续费

309.最佳买卖股票时机含冷冻期 需要新添加状态 class Solution { public:int maxProfit(vector<int>& prices) {int n prices.size();if (n 0) return 0;vector<vector<int>> dp(n, vector<int>(4, 0));dp[0][0] - prices[0]; // 持股票for (i…

[.NET项目实战] Elsa开源工作流组件应用(三):实战演练

补充 之前的文章简单介绍了工作流和Elsa工作流库&#xff0c;这里再补充说明两点 工作流的使用场景非常广泛&#xff0c;几乎涵盖了所有需要进行业务流程自动化管理的领域。 学习一个开源库&#xff0c;最简单的方法就是看源码&#xff0c;Elsa的工作流引擎源码非常简单易懂&…

【Flutter 面试题】讲一讲 Dart 的一些重要概念?

【Flutter 面试题】讲一讲 Dart 的一些重要概念&#xff1f; 文章目录 写在前面口述回答补充说明完整代码运行结果详细说明 写在前面 &#x1f64b; 关于我 &#xff0c;小雨青年 &#x1f449; CSDN博客专家&#xff0c;GitChat专栏作者&#xff0c;阿里云社区专家博主&#…

【Leetcode每日一题】 递归 - 反转链表(难度⭐)(36)

1. 题目解析 题目链接&#xff1a;206. 反转链表 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 一、递归函数的核心任务 递归函数的主要职责是接受一个链表的头指针&#xff0c;并返回该链表逆序后的新头结点。递归…

mysql迁移达梦数据库 Java踩坑合集

达梦数据库踩坑合集 文章目录 安装达梦设置大小写不敏感Spring boot引入达梦驱动&#xff08;两种方式&#xff09;将jar包打入本地maven仓库使用国内maven仓库&#xff08;阿里云镜像&#xff09; 达梦驱动yml配置springboot mybatis-plus整合达梦,如何避免指定数据库名&…

如何在Windows系统使用VS Code制作游戏网页并实现无公网IP远程访问

文章目录 前言1. 编写MENJA小游戏2. 安装cpolar内网穿透3. 配置MENJA小游戏公网访问地址4. 实现公网访问MENJA小游戏5. 固定MENJA小游戏公网地址 前言 本篇教程&#xff0c;我们将通过VS Code实现远程开发MENJA小游戏&#xff0c;并通过cpolar内网穿透发布到公网&#xff0c;分…

YZ系列工具之YZ08:窗体加载图片后进行放大查看

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

Linux学习总结下

vim\vi编辑器 什么是vi\vim编辑器&#xff1f; 1、vi、vim编辑器&#xff0c;就是命令模式下的文本编辑器&#xff0c;用来编辑文件 2、vim是vi的升级版&#xff0c;一般用vim即可&#xff0c;包含vi所有功能 基础命令&#xff1f; vi 文件路径 vim 文件路径 运行模式 …

二、yocto 集成ros2(基于raspberrypi 4B)

yocto 集成ros2 yocto 集成ros21. 下载ros layer2. 编译集成ros3. 功能验证 yocto 集成ros2 本篇文章为基于raspberrypi 4B单板的yocto实战系列的第二篇文章。 一、yocto 编译raspberrypi 4B并启动 本节我们将ros2机器人操作系统移植到我们的yocto系统里面。 1. 下载ros laye…

LLM如何处理长上下文:Lost in the middle

论文地址&#xff1a;Lost in the Middle: How Language Models Use Long Contexts 论文总结&#xff1a;写prompt的时候&#xff0c;需要注意内容的顺序&#xff0c;把重要的信息放在最前面或者最后面。 大型语言模型大有用处&#xff0c;在设计 prompt 方面&#xff0c;人们…

当OKR无法按时完成或达成时,应如何进行调整?

在企业管理中&#xff0c;OKR&#xff08;Objectives and Key Results&#xff0c;目标与关键成果&#xff09;作为一种有效的管理工具&#xff0c;被广泛用于设定和跟踪目标。然而&#xff0c;在实际执行过程中&#xff0c;OKR无法按时完成或达成的情况时有发生。面对这种情况…