函数的栈帧

       我们每次在调用函数的时候,都说会进行传参。每次创建函数,或者进行递归的时候,也会说会进行压栈。

       那么,今天我们就来具体看看函数到底是如何进行压栈,传参的操作。

什么是栈?

       首先我们要知道,我们将内存一般划分为三个区域:

  • 静态区
  • 堆区
  • 栈区

       我们平时创建的临时变量,函数都会在栈区中占据空间:

       此时我们也要知道栈区的使用规则:从高地址向低地址使用

栈的使用规则:

       我们知道抢的弹夹,我们要逐个把子弹往里面压,之后如果取出子弹,就需要将上一次压入的子弹取出,之后逐个取出子弹,并只能按照顺序取出。

       栈就是这样的使用规则,遵循先进后出,后进先出。        此时你会想,不能把任意的数据取出,必须一个一个拿,这种结构真的好用吗?

        起初我也这样认为,但是计算机就喜欢用这种结构。

        在内存中,栈区的使用规则是从高地址向低地址使用的。

函数的栈帧:

       C语言中,我们要想观察函数栈帧就需要用到调试。当我们调试时所在的函数(此时函数未运行完),每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。

       因为我们知道,只要运行函数就会进行压栈操作,所以分析出一下信息:

  1. 栈帧是一块因函数运行而创建的的临时空间。
  2. 每调用一次函数都会创建一个独立的函数栈帧。
  3. 栈帧中存放着函数重要信息,如局部变量,函数返回地址,函数参数等。
  4. 当函数运行完毕后栈帧会销毁。

       既然会创建函数栈帧,那么就会维护其空间,计算机使用寄存器维护空间。

什么是寄存器?

       这里牵扯很多内容,我们只给出笼统解释:寄存器是集成到CPU上的,是独立的,寄存器可以暂存指令,地址和数据。所以寄存器也可以理解为指针。

       我们会使用很多寄存器,要理解清楚函数栈帧,就必须理解ebp和esp,这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。我们详细来讲esp和ebp两个寄存器。其余寄存器混个脸熟即可,一会会用到。

esp寄存器:

       维护栈顶,始终指向栈顶(此时esp寄存器存储栈顶地址)。

ebp寄存器:

       维护当前函数栈帧的栈低(此时ebp寄存器存储函数栈帧栈低地址)。

几个必要的汇编指令:

       我们观察函数栈帧的创建和销毁,就要知道几个汇编指令,这样可以更好的阅读以下内容。

       记不住没关系,我们一下会一一讲解。 

图解: 

        这里我们使用VS2013来观察,由于VS2022太过高级,有些内部细节就会看不到,所以用VS2013来观察。此时我们执行以下代码:

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

       esp和ebp就是维护当前调用的函数。 

       点击F10开始调试。       接下来我们看main函数被谁调用了,我们进入main函数,并直接执行完。

       在VS2013中,main函数也是被其他函数调用的。没想到main函数也是被调用的函数,也理解了为什么每次都要有返回值。 

       mainCRTStartup函数调用 __tmainCRTStartup函数,__tmainCRTStartup函数调用main函数。

       之后我们按住F10,之后右击鼠标找到“转到反编汇”,就可以找到C语言所对应的汇编代码,箭头的指向就可以一行一行的执行,我们来逐过程分析:

       

        push ebp,将ebp压入栈区。因为esp维护栈顶,所以esp指向改变。我们可以观察其存放地址的改变。

       之后执行move,move是把后面的值赋到前面去。

       此时ebp和esp指向的位置相同。

        之后,就要创建函数的栈帧了,执行sub,就是将其寄存器存放地址减去一个地址,因为栈区是高地址到底地址,所以该地址向上。

 

       此时esp维护main函数的栈顶,ebp维护main函数的栈低。我们可以看内存:

       这些内存都是为main函数开辟的空间。

       之后有执行了3次push,push时会有一个动作,就是栈顶指针esp会变,一直指向栈顶。

       我们通过内存窗口来观察:        因为我们说过,寄存器既可以存地址,也可以存数据,此时ebx存的是数据(就是内存里面的内容),而esp存放的是ebx的地址。压入ebx以后,继续将esi、edi压入。

       之后执行lea : load effective address 加载有效地址。        此时会发现,正好是加载的空间正好是最开始esp减去的空间,就是main函数栈帧的低地址。

       之后执行的命令,我们就需要先讲解一下了。

       我们应该听过字节的概念,1字节等于8比特位,那么字和字节又什么关系?一个字等于两个字节。

       比特记为bit,字节记为Byte,字记为word,所以有如下关系:

  • 1Byte=8bits
  • 1word=2Bytes=16bits 
  • dword:一个word是两个字节,d代表double,就是双字,就是4个字节。

       此时我们要看其以下的三个步骤:

        此时edi里面存放main函数栈帧的低地址。

        这样就可以理解为什么每次打印未初始化的空间,打印出来的字符都是一个汉字“烫烫烫烫”了。

       此时才会开始执行有效的代码。在此之前都是为main函数开辟的空间。

       此时就要调用Add函数了,一样的,我们要改变ebp和esp的指向,因为进入Add函数就需要维护Add函数栈帧了,但是还是要做以下准备,就是传参,我们来看形式参数的创建。

       这两个动作相当于传参,之后执行call,就是调用函数,要记住call的地址,此时点击F11才能进入Add函数。

       我们可以发现就在ecx的下一个地址里面存储了call指令下一个执行的地址。为什么要记录地址?我们先埋个伏笔,此时我们会先进入Add函数,流程如下:

       注意此时main函数的函数栈帧已经增长到call指令的下一个地址了。        此时我们来观察Add函数的细节:将esp减去一个地址改变指向:

       之后还是main函数栈帧的那一套操作,压入3个寄存器并初始化空间,并将z初始化为0: 

       此时先将 ebp + 8 的值赋给 eax ,此时 eax = 10;之后又执行add,将 ebp + 12 的值等于30,最后将eax的值赋给 ebp - 8 ,此时 ebp - 8 地址的值是30.        我们可以发现,我们使用Add函数并没有创建形参,在我们传参时其实已经压栈过了,而且参数是从右向左传参的。

       返回的话z会被销毁,我们来观察其如何返回。

       我们将结果放入eax寄存器当中,此时就不用担心函数销毁。

       此时将上面的3个寄存器弹出栈顶。

       之后mov esp的位置,esp的指向改变:        此时弹出ebp,ebp弹出以后会指向main函数的栈低,因为之前记录着mian函数的栈低。

 

        当前栈顶元素为call指令的下一个指令的地址,ret这条指令就是找到之前call指令记录的地址,并pop一次栈顶元素。

        此时执行add   esp,8 因为没有dword 所以是改变指向。

       此时将形参x,y的空间还给操作系统。此时又执行mov,将eax存放的值赋给 ebp - 20h 就是给c赋值。 

       此时main函数执行完,也是以上步骤,我们不再赘述。

总结: 

       我们通过观察函数栈帧的创建和销毁,最后返回值是由寄存器带回来的;也可以理解为什么局部变量的值是随机的,形参和实参的关系,确实是一份临时拷贝。希望大家下去多加练习,逐渐就会顿悟其中的原理。

       爆肝一整天,点点赞吧,呜呜~

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

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

相关文章

基于SSM+MySQL学生宿舍管理系统的设计与实现(源码+数据库+文档)

摘 要 近年来,随着计算机技术的不断发展和运用,许多实际问题都得到了较好地解决。随着现代社会对企业经营的需求日益增长,企业的无纸办公也逐渐得到了推广。本学生宿舍管理系统的设计开发,目标就是解决宿舍管理复杂的人为管理&a…

聚观早报 |极氪金砖电池发布;微信湾事通小程序上线

【聚观365】12月11日消息 极氪金砖电池发布 微信湾事通小程序上线 华为自拍专利曝光 鸿蒙智行App上架华为应用市场 苹果“播客”应用将登陆特斯拉汽车 极氪金砖电池发布 极氪汽车官方此前宣布极氪能源日 2023 暨电池新品发布会将于 12 月 14 日举行,slogan 为…

钓鱼网站域名识别工具dnstwist算法研究

先上一个AI的回答: dnstwist是一种钓鱼网站域名识别工具,可帮助用户识别和检测可能被恶意使用的域名。它通过生成类似的域名变体来模拟攻击者可能使用的钓鱼域名,并提供了一系列有用的功能和信息。 dnstwist能够生成一组类似的域名变体&…

管理类联考——数学——真题篇——按知识分类——几何

文章目录 2023真题(2023-07)-几何-解析几何-最值真题(2023-10)-几何-立体几何-正方体:体积: V a 3 Va^3 Va3;表面积: S 表 6 a 2 S_表6a^2 S表​6a2;体对角线外接球的半…

电源模块测试系统测试LED电源项目的优势

LED电源测试是电源在设计、生产过程中的关键环节,也是确保LED照明产品可靠性和稳定性的重要步骤。LED电源测试一般包括电压、电流、效率、稳定性等。电源模块测试系统测试LED电源,实现自动化测试,保证测试结果的可靠性。 LED电源测试项目及方…

低代码云MES系统才是工业互联网的未来

众所周知,传统制造业目前已更多向智能化、绿色化方向发展。减少资源浪费、跟踪产品信息、提升生产品质、减少库存数量等有益企业的决策,成为管理层所考虑的企业基本问题。因此,制造业现场需解决此类难题才能带动企业数字化转型,以…

NXP应用随记(一):S32K3xx手册阅读随记

目录 1、概述 1.1、S32k组合和应用程序 1.2、技术概述 2、Safety 3、Low Power 3.1、RUN mode 3.2、Standby mode 4、外设概述与互联 ADC WDG STM PIT 1、概述 1.1、S32k组合和应用程序 核心和平台:跨S32K3系列的ArmCortex-M7核心,用于软件重用 安全性…

Python 数据库操作SQL基础

文章目录 SQL 基础数据库和表的创建数据的插入、查询、更新和删除索引、连接和子查询 Python 中的数据库操作关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战案例③Pytho…

tamcat乱码

学习springmvc时tamcat乱码 ①、启动时tomcat控制台乱码 解决方法是:1、先把idea设置里的默认字节码改成utf-8 ​ 2、把idea显示编码改成utf-8,在末尾加上( -Dfile.encodingUTF-8) ​ 3、最后重启idea 加上这个 -Dfile.encodingU…

promethesu告警规则配置,alertmanager通过webhook通知

文章目录 前言一、promethesu告警二、告警配置编写rule文件prometheus配置prometheus产生告警 三、告警通知prometheus 配置 alertmanageralertmanager 配置 webhook通知编写接口接收 webhook 总结 前言 如果没有学习过prometheus的基础和监控的同学,可以先过一遍这…

利用工具JStack排查:死锁问题和CPU100%问题

无论是再面试过程中还是再实际项目开发当中我们都有可能遇到这两个问题。我之前有同学面试这两个问题都有问道过。哈哈哈。所以我绝对把他们了解下并利用博客记录。 1、工具JStack是什么? JStack可以显示Java应用程序中每个线程的堆栈跟踪,帮助开发人员…

Linux系统中的信号

文章目录 01. 学习目标02. 信号的概述信号的概念信号的特点 03. 信号的编号(了解)04. 信号四要素05. 信号的状态1) 产生2) 未决状态:没有被处理3) 递达状态:信号被处理了 06. 阻塞信号集和未决信号集6.1 阻塞信号集(信号屏蔽字)6.2 未决信号集 07. 信号产…

Win10操作系统安装Python

1 Python解释器下载 1.1 安装环境 Windows 10 专业工作站版22H2 python-3.9.6-amd64.exe 1.2 下载地址 Python官网:Welcome to Python.org Python镜像:CNPM Binaries Mirror 2 Python解释器安装 2.1 Install Python 3.9.6 (64-bit)界面 双击运行下…

【FAQ】推送前台应用的通知处理功能没生效,如何进行排查?

一、前台应用的通知处理简介 在调用推送接口时可以设置“foreground_show”字段控制前台应用的通知栏消息是否通过NC展示。“foreground_show”默认值为“true”,应用在前台时由NC展示通知栏消息;当设置为“false”时,应用在前台时&#xff…

骁龙8 Gen 3 vs A17 Pro

骁龙8 Gen 3 vs A17 Pro——谁会更胜一筹? Geekbench、AnTuTu 和 3DMark 等基准测试在智能手机领域发挥着至关重要的作用。它们为制造商和手机爱好者提供了设备性能的客观衡量标准。这些测试有助于评估难以测量的无形方面。然而,值得注意的是&#xff0c…

2024年江苏省职业院校技能大赛信息安全管理与评估 理论题(样卷)

2024年江苏省职业院校技能大赛信息安全管理与评估 理论题(样卷) 理论技能与职业素养(100分) 2024年江苏省职业院校技能大赛(高职学生组) 模块三“信息安全管理与评估”理论技能 【注意事项】 Geek极安云…

分布式锁实现方案 - Lock4j 使用

一、Lock4j 分布式锁工具 你是不是在使用分布式锁的时候,还在自己用 AOP 封装框架?那么 Lock4j 你可以考虑一下。 Lock4j 是一个分布式锁组件,其提供了多种不同的支持以满足不同性能和环境的需求。 立志打造一个简单但富有内涵的分布式锁组…

java web系统的常见安全问题

一、背景 java开发的系统在发布到互联网后都需要进行安全扫描,本文主要总结开发web系统需要注意的与系统安全相关的问题。因为在做需求开发时,很少产品会将系统安全的因素考虑在内,总觉得实现个需求很简单,就是一些页面&#xff0…

Linux 文件权限

背景 因为在做一个任务的时候,调接口要到某个路径下的文件下记录log,但是调接口总是报一个错误: SEVERE: Local file cretaion error! I/O exception! File: /xxx/xxx/xxx/xx.log发现是一个创建文件的错误,根据提示,…

PC端视频网站系统源码 系统自带采集功能 附带完整的搭建教程

近年来,视频内容已成为人们日常生活中不可或缺的一部分。下面罗峰给大家分享一款PC端视频网站系统源码,该系统具有强大的采集功能,可轻松实现视频内容的自动更新和丰富。并附带完整的搭建教程,帮助您快速构建属于自己的视频网站。…