函数栈帧的创建与销毁

函数栈帧的创建与销毁

  • 前言
  • 认识相关寄存器
  • 认识相关汇编命令
  • 详解思路图

前言

函数栈帧的创建与销毁在不同编译器下,函数调用过程中栈帧的创建略有差异,具体细节取决于编译器的实现,但大体逻辑是一致的。(在使用编译器时,建议不要使用太高级的编译器,编译器越高级,越智能,越不容易观察(函数栈帧的过程封装的越不好去看)。)

认识相关寄存器

esp:栈顶指针
ebp:栈底指针

eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
ebp:栈底寄存器
esp:栈顶寄存器
eip:指令寄存器,保存当前指令的下一条指令的地址

注:

  • esp,ebp这两个寄存器存放的是地址,这两个地址是用来维护函数栈帧的。
  • 每一个函数调用都要在栈区创建一个空间
  • 在调用哪个函数,esp和ebp寄存器维护的就是哪个函数的函数栈帧,这两个寄存器之间的空间就是为这次函数调用所分配的空间(函数栈帧)
  • 栈区的使用习惯是先使用高地址再使用低地址

认识相关汇编命令

mov:数据转移指令
push:数据入栈,同时esp栈顶寄存器也要发生改变
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
sub:减法命令
add:加法命令
call:函数调用,1. 压入返回地址 2. 转入目标函数
jump:通过修改eip,转入目标函数,进行调用
ret:恢复返回地址,压入eip,类似pop eip命令

详解思路图

代码:

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

main函数也是要被调用的

在VS2019中main函数是被其他函数调用的,以下是调用main函数的函数
在这里插入图片描述

在这里插入图片描述

在VS2013中main函数是被_tmainCRTStartup这个函数调用的,而_tmainCRTStartup函数是被mainCRTStartup函数调用的。

调用main函数的函数栈帧以及main函数栈帧的开辟:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

补充:

  • 压栈(push)是给栈顶放一个元素
  • 出栈(pop)是从栈顶拿出一元素

main函数中有效的代码:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

注:以下图中因为是函数栈帧的销毁如果删除部分信息导致不易查看,函数栈帧的划分查看以上图(因为是函数栈帧销毁的过程栈帧范围不易表明)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

main函数栈帧的销毁:

在这里插入图片描述

总结:

  • 局部变量是怎么创建的?
    局部变量的创建首先为这个函数分配好栈帧空间,栈帧空间里面初始化好一部分空间之后,然后给局部变量在栈帧空间里分配一点空间
  • 为什么局部变量的值是随机值?
    因为随机值是放进去的,局部变量如果不初始化,栈帧空间里面的值是随机放进去的。这时如果局部变量初始化,这时就把随机值覆盖了。
  • 函数是怎么传参的?传参的顺序是怎样的?
    当调用函数时其实还没有调用时已经把要传递的参数从右向左开始压栈压进去了,当进入函数时在函数栈帧里通过指针偏移量可以找到形参。
  • 形参和实参是什么关系?
    形参确实是压栈的时候开辟的空间,它和实参只是值上是相同的,空间是独立的,所以形参是实参的一份临时拷贝。改变形参不会影响实参。
  • 函数调用是怎么做的?
  1. 将要传递的参数从右向左赋值给相应的寄存器中,然后从右向左进行相应的压栈。
  2. 将call指令的下一条指令的地址压栈(目的是函数调用完之后,能够回到call指令的下一条指令中去)
  3. 进入相应的函数栈帧时,把调用方所在函数栈帧的ebp压栈,创建函数栈帧
  4. 当在函数栈帧中用到形参时,会根据相应的寄存器(ebp)的偏移量找到参数的值
  5. 函数结束时,会将返回值(局部变量的值)通过寄存器(eax)进行保存
  6. 函数结束时,销毁函数栈帧,将ebp赋值给esp,再将栈顶的数据(之前存储调用方函数栈帧ebp的值)出栈赋给ebp同时esp也指到了相应的位置。此时esp和ebp寄存器开始维护调用方函数的栈帧空间
  7. 通过ret指令(ret指令的作用是从栈顶弹出了call指令的下一条指令地址然后跳到那去)回到call指令的下一条指令地址中,继续执行调用方所在函数中没有执行的语句。
  8. 将调用该函数的返回值(通过寄存器eax进行赋值)赋值给调用方
  9. 函数调用结束
  • 函数调用是结束后怎么返回的?
    调用之前就把call指令的下一条指令的地址压栈了,把ebp调用这个函数的上一个函数的栈帧的ebp存进去了,当函数调用完返回时出栈ebp就能够找到原始(上一个函数)调用的ebp,然后指针往下走的时候就能够找到esp的地址—回到栈帧空间。记住call指令的下一条指令的地址,当往回返的时候就可以跳转到call指令的下一条指令的地址,让函数调用的时候可以返回,返回值是通过寄存器的方式带回来的。

注:

  1. 调用函数,需要先形成临时拷贝,形成过程是从右向左的
  2. 临时空间的开辟,是在对应函数栈帧内部开辟的
  3. 函数调用完毕,栈帧结构被释放掉
  4. 临时变量具有临时性的本质:栈帧具有临时性
  5. 调用函数是有成本的,成本体现在时间和空间上,本质是形成和释放栈帧有成本
  6. 函数调用,因拷贝所形成的临时变量,变量和变量之间的位置关系是有规律的
  • 寄存器是集成到CPU上的
  • 函数的地址不是ebp地址。
  • 编译器自己会主动计算要多大空间(不会出现函数预开辟空间不够用这一现象),每个函数预开辟空间的大小是不一样的,这时编译器做的,开辟多大空间是不确定的。
  • 函数的形参是放在调用方的函数栈帧中的,这些空间增长开辟都是在调用方的函数栈帧中的完成的。

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

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

相关文章

某游戏登录密码加密,webpack

注意&#xff1a;文章内容仅用于学习和技术交流&#xff0c;切勿做出违法的事情&#xff0c;如有侵权请联系我删除。 网址&#xff08;今天的大冤种&#xff09;&#xff1a;aHR0cHM6Ly93d3cuZ205OS5jb20v 一&#xff0c;分析 从上面图片可以看到&#xff0c;他的密码是加密了…

桥接模式(十)

不管怎么样&#xff0c;都要继续充满着希望 上一章简单介绍了适配器模式(九), 如果没有看过, 请观看上一章 一. 桥接模式 引用 菜鸟教程里面的 桥接模式介绍: https://www.runoob.com/design-pattern/bridge-pattern.html 桥接&#xff08;Bridge&#xff09;是用于把抽象化…

谷粒商城p46-配置网关路由与路径重写

软件 &#xff1a; vscode idea 服务&#xff1a; renren-fast&#xff0c;gulimall-product&#xff0c;gulimall-gateway、nacos 前提条件&#xff1a; gateway、renren-fast已经注册到nacos 注意&#xff1a; 1、renren-fast单独注入nacos依赖&#xff0c;不要注入common…

#2023开放原子全球开源峰会之旅

#2023我在开源峰会 2023开放原子全球开源峰会参会指南 嗨咯&#xff0c;大家好&#xff01; 6月11号&#xff0c;是一年一度的开放原子大会&#xff0c;有幸参加&#xff0c;很开心&#xff01; 文章目录 1、逛展区&#xff08;领周边&#xff09;环节1.1 CSDN展区1.2 阿里云 …

ansible的部署和命令模块

一、 ansible 的概述 1、ansible简介 Ansible是一款为类Unix系统开发的自由开源的配置和自动化工具。 它用Python写成&#xff0c;类似于saltstack和Puppet&#xff0c;但是有一个不同和优点是我们不需要在节点中安装任何客户端。 它使用SSH来和节点进行通信。Ansible基于 …

(九)CSharp-数组

一、矩形数组 1、访问数组元素 class Program{static void Main(string[] args){int[] intArr1 new int[15];intArr1[2] 10;int var1 intArr1[2];int[,] intArr2 new int[5, 10];intArr2[2, 3] 7;int var2 intArr2[2, 3];int[] myIntArray new int[4];for (int i 0; i…

计算字母出现次数【存在括号计算】

计算字母出现次数【存在括号计算】 此代码考虑到了本问题的大多可能情况&#xff0c;闲话少述&#xff0c;代码中的注释很丰富。 代码绝对可以解决你的问题&#xff01; 不行你就评论&#xff0c;回复速度超快 作者时间YaoChongChong2023年6月14日10&#xff1a;40 Descript…

【gcc, cmake, eigen, opencv,ubuntu】五.CMakeLists.txt编写

文章目录 CMakeLists.txt编写1.CMakeLists.txt模板2.设置编程语言版本3.设置编译类型Debug&#xff0c;Release4.设置获取文件列表5.添加include目录6.配置编译选项 CMakeLists.txt编写 1.CMakeLists.txt模板 一个使用opencv 的 CMakeLists.txt # cmake最低版本要求 cmake_m…

该怎么学Python?自学Python的方法和资料整理!

导语 Python 作为一门简洁、易学且功能强大的编程语言&#xff0c;备受程序员和初学者的喜爱。如果你也想学习 Python&#xff0c;但不知从何入手&#xff0c;本文将为你整理一些自学 Python 的方法&#xff0c;助你快速入门并掌握这门语言。 为什么学习Python&#xff1f;&a…

requests库的使用

文章目录 get 请求post 请求get 请求和 post 请求的区别response1. res.headers2. status_code3. json get 请求 参数类型作用urlstr发起请求的地址params字典url为基准地址&#xff0c;不包含查询参数&#xff1b;使用此参数会自动对 params 字典编码&#xff0c;然后和url拼…

函数参数的拓展

函数参数的默认值 C 中可以在函数声明时为参数提供一个默认值 当函数调用时没有提供默认参数的值&#xff0c;则使用默认值 参数的默认值必须在函数声明中指定 当函数声明时没有出现参数的默认值&#xff0c;而定义的时候出现参数的默认值&#xff0c;编译器会报错 当函数声…

看了这几个C语言例子,你一定和我一样连说5个卧槽,声音一次比一次大

曾经我一直以为自己C语言学的还挺好的&#xff0c;直到看到这几个例子。 例1 首先来看一下&#xff0c;大师是如何求圆周率的&#xff0c;一口君实在词穷&#xff0c;first卧槽。 #include <stdio.h>long a10000,b0,c10000,d,e,f[10001],g;void main(){for(;b ! c; f[…

nginx的安装及代理和负载均衡设置

一、通过yum方式进行安装 官网参考地址&#xff1a;https://nginx.org/en/linux_packages.html#RHEL 1.1 安装好依赖 执行下面的命令安装 sudo yum install yum-utils1.2、 先配置好yum源 新建文件/etc/yum.repos.d/nginx.repo&#xff0c;文件内容&#xff1a; [nginx-s…

Spark SQL数据源:Hive表

文章目录 一、Spark SQL支持读写Hive二、Spark配置hive-site.xml三、准备工作&#xff08;一&#xff09;启动Hive的metastore&#xff08;二&#xff09;启动Spark Shell 四、Spark读写Hive数据&#xff08;一&#xff09;导入SparkSession&#xff08;二&#xff09;创建Spar…

内网安全:Cobalt Strike 与 MSF 联动( 会话 相互转移 )

内网安全&#xff1a;Cobalt Strike 与 MSF 联动&#xff08; 会话 相互转移 &#xff09; 在渗透中&#xff0c;有时候 Cobalt Strike 会话可能会受限制&#xff0c;所以我们需要把 Cobalt Strike 会话转移到 MSF 上进行后面的渗透。也有的时候会话在 MSF 上&#xff0c;但是…

MySQL数据库的认识及基础命令操作

目录 一、数据库的基本概念 1、数据库定义 &#xff08;1&#xff09; 数据 &#xff08;2&#xff09;表 &#xff08;3&#xff09; 数据库 2、 数据库管理系统&#xff08;DBMS&#xff09; 3、 数据库系统&#xff08;DBS&#xff09; 二、数据库系统发展史 1、 第一…

编程必备:JAVA多线程详解

目录 前言 1.入门多线程 1.1. 线程、进程、多线程、线程池 1.2.并发、串行、并行 1.3. 线程的实现方式 1.3.1. 继承 Thread 类 1.3.2. 实现 Runnable 接口 1.3.3. 使用 Callable 和 Future 1.3.4. 使用线程池 1.4.线程的状态 1.5. 线程常用方法 1.5.1 sleep() 1.4…

docker 网络理论知识点 - CNM 和命名空间

Network 目录 1 network namespace1.1 动手小实验 2 回到 docker2.1 driver and docker02.2 network2.3 网桥 docker0 3 总结 1 network namespace 1.1 动手小实验 网络命名空间。linux kernel 提供的网络虚拟化的功能。创建多个隔离的网络空间。每个空间内 firewall, ether …

【taro react】---- 解决H5接入uni-app版本的IM

1. 问题 由于项目开发比较紧张&#xff0c;腾讯 IM 的接入就使用了 TUIKit 含UI集成方案&#xff0c;遇到的问题&#xff0c;uni-app的UI本来就是一个单独的项目&#xff0c;需要集成到现有的 Taro React 中&#xff0c;就只能作为一个独立的项目&#xff0c;不跳转时不影响原有…

在十四届蓝桥杯开赛前一星期开始复习

文章目录 十三届蓝桥杯国赛原题1.20222.钟表3卡牌4最大数字4.5 Dijkstra算法5出差 十三届蓝桥杯国赛原题 1.2022 #include<iostream> using namespace std;long long int f[2023][11][2023]; //表示前2022个物品选择10个物品&#xff0c;体积总和为2022的方案个数 ,,数…