做it的兼职网站/公司seo推广营销网站
目录
引言
基础知识
内存模型
寄存器的种类与功能
常用的汇编指令
函数栈帧创建与销毁
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对栈进行操作。比如获取函数参数,局部变量等。其内存放一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
- esp和ebp这两个寄存器放置的是地址,这两个地址是用来维护函数栈帧的。每个函数调用,都需要在栈区创建一个空间。当正在调用某函数时,esp和ebp就维护这个函数栈帧的空间。
- 栈区使用:从高地址向低地址消耗/使用。
常用的汇编指令
这里我们只讲解几个我们函数栈帧创建等的汇编指令
函数栈帧创建与销毁
了解了上面的基础知识,我们先大致来看下函数栈帧怎样创建与销毁 。
首先我们有想过一个问题吗?就是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()函数栈帧的创建 …...

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

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

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

xlsx xlsx-style file-saver 导出json数据到excel文件并设置标题字体加粗
xlsx:用于处理Excel文件。xlsx-style:用于添加样式到Excel文件中。file-saver:用于将生成的Excel文件保存到用户的计算机上 npm install xlsx xlsx-style file-saver// 导入所需库 const XLSX require(xlsx); const XLSXStyle require(xls…...

Win11游戏高性能模式怎么开
1、点击桌面任务栏上的“开始”图标,在打开的应用中,点击“设置”; 2、“设置”窗口,左侧找到“游戏”选项,在右侧的选项中,找到并点击打开“游戏模式”; 3、打开的“游戏模式”中,找…...

深度学习最强奠基作ResNet《Deep Residual Learning for Image Recognition》论文解读(上篇)
1、摘要 1.1 第一段 作者说深度神经网络是非常难以训练的,我们使用了一个残差学习框架的网络来使得训练非常深的网络比之前容易得很多。 把层作为一个残差学习函数相对于层输入的一个方法,而不是说跟之前一样的学习unreferenced functions 作者提供了…...

第22次CCF计算机软件能力认证
第一题:灰度直方图 解题思路: 哈希表即可 #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语言中有丰富的数据类型,除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。Go 语言的基本类型和其他语言大同小异。 基本数据类型 整型 整型分为以下两个大类: 按…...

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

iOS自定义下拉刷新控件
自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件,想写一个玩玩,自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些,毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下: 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(非api调用) 第一种方法:使用Docker本地化部署第一步,下载安装Docker登录GPT 第二种方法:不部署项目,直接连接 第一种方法:使用Docker本地化部署 这种方法的好处就是没有登录限…...

前馈神经网络dropout实例
直接看代码。 (一)手动实现 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:安全存储和轻松管理数据
关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。 目录 一、导读二、概览三、使用3.1 Preferences DataStore添加依赖数据读…...

opencv进阶12-EigenFaces 人脸识别
EigenFaces 通常也被称为 特征脸,它使用主成分分析(Principal Component Analysis,PCA) 方法将高维的人脸数据处理为低维数据后(降维),再进行数据分析和处理,获取识别结果。 基本原理…...

The internal rate of return (IRR)
内部收益率 NPV(Net Present Value)_spencer_tseng的博客-CSDN博客...

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

【C++入门到精通】C++入门 —— deque(STL)
阅读导航 前言一、deque简介1. 概念2. 特点 二、deque使用1. 基本操作(增、删、查、改)2. 底层结构 三、deque的缺陷四、 为什么选择deque作为stack和queue的底层默认容器总结温馨提示 前言 文章绑定了VS平台下std::deque的源码,大家可以下载…...

Codeforces Round 893 (Div. 2) D.Trees and Segments
原题链接:Problem - D - Codeforces 题面: 大概意思就是让你在翻转01串不超过k次的情况下,使得a*(0的最大连续长度)(1的最大连续长度)最大(1<a<n)。输出n个数&…...

SpringBoot + Vue 前后端分离项目 微人事(九)
职位管理后端接口设计 在controller包里面新建system包,再在system包里面新建basic包,再在basic包里面创建PositionController类,在定义PositionController类的接口的时候,一定要与数据库的menu中的url地址到一致,不然…...

【业务功能篇71】Cglib的BeanCopier进行Bean对象拷贝
选择Cglib的BeanCopier进行Bean拷贝的理由是, 其性能要比Spring的BeanUtils,Apache的BeanUtils和PropertyUtils要好很多, 尤其是数据量比较大的情况下。 BeanCopier的主要作用是将数据库层面的Entity转化成service层的POJO。BeanCopier其实已…...

让eslint的错误信息显示在项目界面上
1.需求描述 效果如下 让eslint中的错误,显示在项目界面上 2.问题解决 1.安装 vite-plugin-eslint 插件 npm install vite-plugin-eslint --save-dev2.配置插件 // vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue import e…...

手摸手带你实现一个开箱即用的Node邮件推送服务
目录 编辑 前言 准备工作 邮箱配置 代码实现 服务部署 使用效果 题外话 写在最后 相关代码: 前言 由于邮箱账号和手机号的唯一性,通常实现验证码的校验时比较常用的两种方式是手机短信推送和邮箱推送,此外,邮件推送服…...

【Linux网络】网络编程套接字 -- 基于socket实现一个简单UDP网络程序
认识端口号网络字节序处理字节序函数 htonl、htons、ntohl、ntohs socketsocket编程接口sockaddr结构结尾实现UDP程序的socket接口使用解析socket处理 IP 地址的函数初始化sockaddr_inbindrecvfromsendto 实现一个简单的UDP网络程序封装服务器相关代码封装客户端相关代码实验结…...

Python学习笔记第六十四天(Matplotlib 网格线)
Python学习笔记第六十四天 Matplotlib 网格线普通网格线样式网格线 后记 Matplotlib 网格线 我们可以使用 pyplot 中的 grid() 方法来设置图表中的网格线。 grid() 方法语法格式如下: matplotlib.pyplot.grid(bNone, whichmajor, axisboth, )参数说明:…...

机器学习与模式识别3(线性回归与逻辑回归)
一、线性回归与逻辑回归简介 线性回归主要功能是拟合数据,常用平方误差函数。 逻辑回归主要功能是区分数据,找到决策边界,常用交叉熵。 二、线性回归与逻辑回归的实现 1.线性回归 利用回归方程对一个或多个特征值和目标值之间的关系进行建模…...

vue启动配置npm run serve,动态环境变量,根据不同环境访问不同域名
首先创建不同环境的配置文件,比如域名和一些常量,创建一个env文件,先看看文件目录 env.dev就是dev环境的域名,.test就是test环境域名,其他同理,然后配置package.json文件 {"name": "require-admin&qu…...

HTML <strike> 标签
HTML5 中不支持 <strike> 标签在 HTML 4 中用于定义删除线文本。 定义和用法 <strike> 标签可定义加删除线文本定义。 浏览器支持 元素ChromeIEFirefoxSafariOpera<strike>YesYesYesYesYes 所有浏览器都支持 <strike> 标签。 HTML 与 XHTML 之间…...

数学建模-模型详解(1)
规划模型 线性规划模型: 当涉及到线性规划模型实例时,以下是一个简单的示例: 假设我们有两个变量 x 和 y,并且我们希望最大化目标函数 Z 5x 3y,同时满足以下约束条件: x > 0y > 02x y < 10…...