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

函数栈帧的创建与销毁

目录

引言

基础知识

内存模型  ​

寄存器的种类与功能

常用的汇编指令

函数栈帧创建与销毁

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】

相关文章:

函数栈帧的创建与销毁

目录 引言 基础知识 内存模型 ​ 寄存器的种类与功能 常用的汇编指令 函数栈帧创建与销毁 main()函数栈帧的创建 NO1. NO2. NO3. NO4. NO5. NO6. main()函数栈帧变量的创建 调用Add()函数栈帧的预备工作——传参 NO1. NO2. NO3. Add()函数栈帧的创建 …...

工业安全生产平台在面粉行业的应用分享

一、背景介绍 面粉行业是一个传统的工业行业&#xff0c;安全生产问题一直备受关注。然而&#xff0c;由于生产过程中存在的各种安全隐患和风险&#xff0c;如粉尘爆炸、机械伤害等&#xff0c;使得面粉行业的安全生产形势依然严峻。为了解决这一问题&#xff0c;工业安全生产…...

Gitlab服务部署及应用

目录 Gitlab简介 Gitlab工作原理 Gitlab服务构成 Gitlab环境部署 安装依赖包 启动postfix&#xff0c;并设置开机自启 设置防火墙 下载安装gitlab rpm包 修改配置文件/etc/gitlab/gitlab.rb&#xff0c;生产环境下可以根据需求修改 重新加载配置文件 浏览器登录Gitlab输…...

【nodejs】用Node.js实现简单的壁纸网站爬虫

1. 简介 在这个博客中&#xff0c;我们将学习如何使用Node.js编写一个简单的爬虫来从壁纸网站获取图片并将其下载到本地。我们将使用Axios和Cheerio库来处理HTTP请求和HTML解析。 2. 设置项目 首先&#xff0c;确保你已经安装了Node.js环境。然后&#xff0c;我们将创建一个…...

xlsx xlsx-style file-saver 导出json数据到excel文件并设置标题字体加粗

xlsx&#xff1a;用于处理Excel文件。xlsx-style&#xff1a;用于添加样式到Excel文件中。file-saver&#xff1a;用于将生成的Excel文件保存到用户的计算机上 npm install xlsx xlsx-style file-saver// 导入所需库 const XLSX require(xlsx); const XLSXStyle require(xls…...

Win11游戏高性能模式怎么开

1、点击桌面任务栏上的“开始”图标&#xff0c;在打开的应用中&#xff0c;点击“设置”&#xff1b; 2、“设置”窗口&#xff0c;左侧找到“游戏”选项&#xff0c;在右侧的选项中&#xff0c;找到并点击打开“游戏模式”&#xff1b; 3、打开的“游戏模式”中&#xff0c;找…...

深度学习最强奠基作ResNet《Deep Residual Learning for Image Recognition》论文解读(上篇)

1、摘要 1.1 第一段 作者说深度神经网络是非常难以训练的&#xff0c;我们使用了一个残差学习框架的网络来使得训练非常深的网络比之前容易得很多。 把层作为一个残差学习函数相对于层输入的一个方法&#xff0c;而不是说跟之前一样的学习unreferenced functions 作者提供了…...

第22次CCF计算机软件能力认证

第一题&#xff1a;灰度直方图 解题思路&#xff1a; 哈希表即可 #include<iostream> #include<cstring>using namespace std;const int N 610; int a[N]; int n , m , l;int main() {memset(a , 0 , sizeof a);cin >> n >> m >> l;for(int …...

Go语言基础之基本数据类型

Go语言中有丰富的数据类型&#xff0c;除了基本的整型、浮点型、布尔型、字符串外&#xff0c;还有数组、切片、结构体、函数、map、通道&#xff08;channel&#xff09;等。Go 语言的基本类型和其他语言大同小异。 基本数据类型 整型 整型分为以下两个大类&#xff1a; 按…...

Linux Tracing Technologies

目录 1. Linux Tracing Technologies 1. Linux Tracing Technologies Linux Tracing TechnologieseBPFXDPDPDK...

iOS自定义下拉刷新控件

自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件&#xff0c;想写一个玩玩&#xff0c;自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些&#xff0c;毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下&#xff1a; tableview.refreshControl XRef…...

Springboot写单元测试

导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintag…...

一篇文章教你使用Docker本地化部署Chatgpt(非api,速度非常快!!!)及裸连GPT的方式(告别镜像GPT)

本地搭建ChatGPT&#xff08;非api调用&#xff09; 第一种方法&#xff1a;使用Docker本地化部署第一步&#xff0c;下载安装Docker登录GPT 第二种方法&#xff1a;不部署项目&#xff0c;直接连接 第一种方法&#xff1a;使用Docker本地化部署 这种方法的好处就是没有登录限…...

前馈神经网络dropout实例

直接看代码。 &#xff08;一&#xff09;手动实现 import torch import torch.nn as nn import numpy as np import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt#下载MNIST手写数据集 mnist_train torchvision.datasets.MN…...

Android DataStore:安全存储和轻松管理数据

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、导读二、概览三、使用3.1 Preferences DataStore添加依赖数据读…...

opencv进阶12-EigenFaces 人脸识别

EigenFaces 通常也被称为 特征脸&#xff0c;它使用主成分分析&#xff08;Principal Component Analysis&#xff0c;PCA&#xff09; 方法将高维的人脸数据处理为低维数据后&#xff08;降维&#xff09;&#xff0c;再进行数据分析和处理&#xff0c;获取识别结果。 基本原理…...

The internal rate of return (IRR)

内部收益率 NPV(Net Present Value)_spencer_tseng的博客-CSDN博客...

半导体自动化专用静电消除器主要由哪些部分组成

半导体自动化专用静电消除器是一种用于消除半导体生产过程中的静电问题的设备。由于半导体制造过程中对静电的敏感性&#xff0c;静电可能会对半导体器件的质量和可靠性产生很大的影响&#xff0c;甚至造成元件损坏。因此&#xff0c;半导体生产中采用专用的静电消除器是非常重…...

【C++入门到精通】C++入门 —— deque(STL)

阅读导航 前言一、deque简介1. 概念2. 特点 二、deque使用1. 基本操作&#xff08;增、删、查、改&#xff09;2. 底层结构 三、deque的缺陷四、 为什么选择deque作为stack和queue的底层默认容器总结温馨提示 前言 文章绑定了VS平台下std::deque的源码&#xff0c;大家可以下载…...

Codeforces Round 893 (Div. 2) D.Trees and Segments

原题链接&#xff1a;Problem - D - Codeforces 题面&#xff1a; 大概意思就是让你在翻转01串不超过k次的情况下&#xff0c;使得a*&#xff08;0的最大连续长度&#xff09;&#xff08;1的最大连续长度&#xff09;最大&#xff08;1<a<n&#xff09;。输出n个数&…...

3种核心价值助你掌控数字记忆:WeChatMsg聊天记录管理工具全解析

3种核心价值助你掌控数字记忆&#xff1a;WeChatMsg聊天记录管理工具全解析 【免费下载链接】WeChatMsg 提取微信聊天记录&#xff0c;将其导出成HTML、Word、CSV文档永久保存&#xff0c;对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending…...

Agent Client Protocol 全景解析腊

1. 核心概念 在 Antigravity 中&#xff0c;技能系统分为两层&#xff1a; Skills (全局库)&#xff1a;实际的代码、脚本和指南&#xff0c;存储在系统级目录&#xff08;如 ~/.gemini/antigravity/skills&#xff09;。它们是“能力”的本体。 Workflows (项目级)&#xff1a…...

【OpenClaw】通过 Nanobot 源码学习架构---()总体韭

核心摘要&#xff1a;这篇文章能帮你 ?? 1. 彻底搞懂条件分支与循环的适用场景&#xff0c;告别选择困难。 ?? 2. 掌握遍历DOM集合修改属性的标准姿势与性能窍门。 ?? 3. 识别流程控制中的常见“坑”&#xff0c;并学会如何优雅地绕过去。 ?? 主要内容脉络 ?? 一、痛…...

图解Simple-BEV核心模块:从2D图像到3D BEV特征图的完整数据流解析

图解Simple-BEV核心模块&#xff1a;从2D图像到3D BEV特征图的完整数据流解析 想象一下&#xff0c;当你驾驶汽车时&#xff0c;眼睛看到的只是前方有限的视野&#xff0c;而大脑却能神奇地将这些二维画面重构为三维空间感知。这正是自动驾驶系统中BEV&#xff08;鸟瞰图&#…...

Windows HEIC缩略图插件:3分钟解决iPhone照片在Windows上的预览难题

Windows HEIC缩略图插件&#xff1a;3分钟解决iPhone照片在Windows上的预览难题 【免费下载链接】windows-heic-thumbnails Enable Windows Explorer to display thumbnails for HEIC/HEIF files 项目地址: https://gitcode.com/gh_mirrors/wi/windows-heic-thumbnails …...

如何轻松地使用隔空投送将iPhone内容传输到Android

过去&#xff0c;安卓和苹果用户在设备间共享文件一直很麻烦。然而&#xff0c;如今迎来了一个重大转折点——我们可以使用 AirDrop 将 iPhone 文件传输到安卓设备。尽管这项功能已经发布几天了&#xff0c;但很多人仍然不知道如何从 iPhone 向安卓设备发送 AirDrop 文件。现在…...

WeChatExporter:开源微信聊天记录备份与查看解决方案

WeChatExporter&#xff1a;开源微信聊天记录备份与查看解决方案 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 微信作为日常沟通的重要工具&#xff0c;承载着大量有价…...

FastAPI异步测试终极指南:从配置到实现的完整教程

FastAPI异步测试终极指南&#xff1a;从配置到实现的完整教程 【免费下载链接】fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production 项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi FastAPI异步测试是构建高…...

从PyTorch到Android:YOLOv11模型轻量化部署与Qt实战避坑指南

1. 为什么选择Qt for Android部署YOLOv11&#xff1f; 对于习惯C开发的工程师来说&#xff0c;用Qt框架做Android端部署是个非常务实的选择。我去年接手一个农业巡检项目时&#xff0c;需要在无人机平板上实时检测作物病害&#xff0c;当时尝试过Android Studio方案&#xff0c…...

ADS DC仿真实战:从零构建电源完整性分析

1. 电源完整性分析为何如此重要&#xff1f; 最近在做一个FPGA板卡项目时&#xff0c;我遇到了一个棘手的问题&#xff1a;板卡在低温环境下频繁出现异常重启。经过排查发现&#xff0c;问题出在核心电源轨的压降上。当环境温度降低时&#xff0c;电源网络的阻抗变化导致供电电…...