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

C语言(函数)—函数栈帧的创建和销毁

目录

前言

补充知识

一、函数线帧是什么?

二、函数线帧的实现(举例说明)

两数之和代码

​编辑两数之和 汇编代码分析

 执行第一条语句

执行第二条语句

执行第三条语句

执行第四、五、六条语句

执行第七条语句

执行第八、九、十条语句

执行第十三条语句

执行第十四、十五条语句

执行第十六条语句

执行第十七条语句

 执行第十七、十八条语句

 执行第十九、二十条语句

进入Add函数里面 

Add函数预开辟空间

创建z

 相加

实际传参

返回z

弹出

回收没用空间,回到main函数

将数放进c中

三、总结

局部变量是怎么创建的,局部变量的创建?

 为什么局部变量的值是随机值?

函数是怎么传参的?传参的顺序是什么样的?

形参和实参是什么关系?

函数调用结果是怎么返回的?


前言

这里补充一下函数线帧的创建和销毁,我们知道函数调用一次就会占用一次栈内存。每一次函数调用都会为本次函数调用分配内存空间(是在内存的栈区),为本次函数调用分配的内存空间叫做被称为这次函数调用的栈帧空间,函数栈帧的创建和销毁。

编译器越高级,那么 就越不容易发现在函数调用的过程中线帧的创建,具体细节取决于编译器的实现。新的编译器由于考虑各种各样的问题,所以封装的更加复杂,不容易分离出来函数栈帧创建的过程。

栈空间的使用是从高地址向地地址增长。

寄存器是集成到cpu上的,跟mian函数是没有关系的,是独立的,硬盘,内存,寄存器是相互独立的。


补充知识

我们知道计算机中有寄存器,而寄存器包括很多,例如eax,ebx,ecx,edx,ebp,esp等。

而ebp,esp这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的。

每一个函数调用,都要在栈区上创建一个空间,而当调用哪个函数,esp和ebp就会维护哪个函数线帧。通常esp称为栈顶指针,sbp称为栈底指针。

当我们在调用main函数之前,会调用_tmainCRTStartup这个函数,这个函数内部调用了main函数,而这个函数又是被mainCRTStartup这个函数调用的。

其实在VS2013中,main函数也是被其他函数调用的。

一、函数线帧是什么?

函数栈帧是指在函数调用过程中,为了保存函数的局部变量、参数和执行上下文等信息而创建的一块存储区。

每次函数调用时,都会在栈上创建一个新的栈帧,用于存储该函数的局部变量、参数和执行上下文等信息。栈帧通常包括以下几个部分:

  1. 局部变量区域:用于存储函数内部定义的局部变量和临时变量等。

  2. 参数区域:用于存储函数的参数值。

  3. 返回地址:用于保存函数调用完成后的返回地址,以便能够返回到调用方继续执行。

  4. 上一级函数的栈帧指针:用于保存上一级函数的栈帧地址,以便能够返回到上一级函数。

  5. 其他上下文信息:如调用方的寄存器值、异常处理信息等。

函数栈帧的创建和销毁是由编译器和操作系统自动完成的,开发者一般无需手动管理。函数栈帧的创建和销毁按照函数调用的顺序,形成一个栈结构,因此也被称为调用栈或执行栈。在函数调用完成后,栈帧被销毁,栈指针回退到上一级函数的栈帧,继续执行上一级函数。这种递归结构的栈帧可以保证函数调用的嵌套和返回顺序的正确性。

二、函数线帧的实现(举例说明)

两数之和代码

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

我们通过用vs2022来进行讲解,例子用的上述代码+两数之和函数。

两数之和 汇编代码分析

我们通过F10来调试代码,之后在代码界面点击右键,选择反汇编,就会出现一个新页面,而这个页面就是图中右侧的,这是C语言对应的汇编代码。

把显示符号取消勾选,因为选上会显示符号名,而我们想要看其地址和布局,所以就去掉。

这时候里面就写的是符号的地址了,而不是符号名字了。

因为main函数是其它函数调用的,所以调用的__tmainCRTStartup已经分配好栈空间了,此时esp和ebp就会维护这个函数线帧(前提下面是高地址,上面是高地址):

 执行第一条语句

意思是将ebp进行压栈,所以栈顶就多了一个元素(ebp),因为esp维护的是栈顶,所以esp指向往上移动了一步。内存就压进去一个ebp。

执行第二条语句

mov的意思就是把后者的值赋给前者,这里也就也是将esp的值赋给ebp,我们知道esp的值赋给了ebp,那么ebp的地址就指向esp,而esp又指向栈顶,ebp就指向了之前esp指的地方,所以就变成了下面:

执行第三条语句

sub是前者减去后者,也就是esp减去后面的数字,0E4h是八进制,esp减去这个数后值变小了,所以esp就指向了上面的某一位置,我们知道esp和ebp之间的就是函数的栈帧空间,所以他们俩之间的就是开辟出来main函数的预留空间,也就是main函数的栈帧。

执行第四、五、六条语句

分别压栈压进去三个元素,ebx,esi,edi,同时esp的值会往上走。每一个push,esp都会往上挪一下。

执行第七条语句

lea实际上是 load effecitive address(加载有效地址)的意思,这里面就是将后面的有效地址加载到edi中,相当于edi里面加上了一个地址。它实际上就似乎找了第三条语句的地址。

执行第八、九、十条语句

 这三步真正有意义的是第三步,前两步是把后面的值放到前面的里面。而第三步则是从edi开始,将39h这么多个空间全部改成eax的内容,每一次初始化(dword个字节,一个4个字节)。实际就是将main函数预开辟的空间的全部数据改成0ccccccch。

自此,main函数的栈帧就已经准备好了。接下来才是操作的代码。

执行第十三条语句

这里就是将0Ah(10) 放到ebp-8这个地址的地方,也就是a的地方,这块地方就放了10

执行第十四、十五条语句

与第十三条语句一样差不多,都是赋值给对应地址。

这就可以总结出函数创建的时候局部变量的创建规律:

首先,创建这个函数的函数栈帧

之后找到一些空间把变量放进去

 接下来就是调用函数,因为函数调用需要传参

执行第十六条语句

从这里我们可以看见后面这个地址与前面的有一个地址一样,这里就是b的地址,所以这里把b的值也就是20放进了eax.

执行第十七条语句

这里进行压栈,把eax压栈,放到栈顶。如下图:

 执行第十七、十八条语句

这里与前两句一样,先把a的值存到ecx中,之后在把ecx进行压栈。 

 执行第十九、二十条语句

执行call指令,会调用一个地址,并且把地址压入栈中,而压入栈中的地址就是下一条指令的地址,也就是add的地址00521918(每次编译可能会不同)。

为什么要调用下一条指令的地址,因为call指令会进入add函数,但进入函数后还需要回来,回来怎么回来,就需要一个地址来进行返回,来回到call指令的下一条指令,再从这个地址往下执行命令。

进入Add函数里面 

Add函数预开辟空间

再往下运行,就进入到了Add函数里面,而上面这些就是为了为Add函数预开辟线帧空间。

与之前开辟main函数的线帧空间一样。开辟之后就如上图所示。

创建z

在ebp-8的地址传入z,这里面放的是0

 相加

我们知道ebp -8就是ecx的位置,ebp-12就是eax的位置,同时ecx里面是a的值,eax里面是b的值,所以给他们给他们命名为是a'和b'。

这里面吧ebp+8的值(10)加到eax里面去,所以eax就为10,之后吧ebp+0ch(12)的值(20)再加入eax中,这时候eax里面就为30,加起来之后再把eax的值放进ebp-8的地址里面去,而z恰好是ebp-8的地址,所以z就为30。

 这里我们发现函数参数x,y并没有,而是通过调用指令进行传参,将形参进行push压栈,压到某一位置,参数是从右向左传的,当真的来到函数内部来调用这两个数相加的时候会发现,形参根本不是在函数内部创建的,而是回来找刚才调入进来传参传入的这个空间,压进去这个空间,上图a'就认为是x,b'就认为是y,也就是x+y,之后传入z的空间。

实际传参

实际传参是还没有调用这个函数的时候,参数a和b就已经传过去了,在函数栈帧中压入了两个参数(b和a),压进去之后,真正用函数内部的时候,其实是再找回之前压入栈中的这两个值,然后相加之后再给z。所以形参是实参的临时拷贝这句话得到了验证。

返回z

因为我们知道函数调用完后会销毁,同时z也会销毁,所以把ebp-8里的值也就是z的值30放进eax寄存器中,寄存器不会因为函数的销毁而销毁,所以放在这里就安全了,先保存起来。

弹出

pop就是弹出的意思,弹出一个esp就往下移动一个地址,之后就如下图了:

这时候发现有些空间没有用到,所以应该回收。

回收没用空间,回到main函数

这里将ebp的位置传入esp,用pop(把栈顶的元素 弹出来放到ebp里面去),因为此时ebp(main地址)存的是之前下面的ebp地址,所以ebp又回到了下面(之前的地方),而esp则指向下call指向下一条指令的地址,因为之前的ebp已经弹出了。

这样就回到了main函数里面,这样栈帧空间又是又esp和ebp开始维护。

现在栈顶元素是call指令下一条指令的地址

这条指令就是回到了call指向下一条指令的地址。再往下执行就是:

esp+8之后,esp就指向了如下图:

当指向这里的时候,就说明把ecx和eax就还给了操作系统。所以这时候形参的空间就还给了操作系统,释放了。

将数放进c中

 这里将eax(计算的和)放进ebp-20h(c)里面去,这样返回值就带了回来。

至此,函数线帧的创建和销毁就结束了。

总图:

三、总结

局部变量是怎么创建的,局部变量的创建?

首先先分配好栈帧空间,然后在这个空间里面分配一些空间来创建局部变量

 为什么局部变量的值是随机值?

因为这个随机值是我们自己放进去的,初始化后就把随机值覆盖了,这时候就不是随机值了

函数是怎么传参的?传参的顺序是什么样的?

当我们要调用这个函数的时候,其实函数还没有调用的时候,就已经把两个参数从右向左push压栈压进去了,当真正进入函数的时候,通过指针的偏移量来找到之前传入的这两个形参参数

形参和实参是什么关系?

形参是实参的一份临时拷贝,形参确实是在压栈的时候开辟的空间,它和实参只是值是相同的、空间是独立的,改变形参是不会影响实参的。

函数调用结果是怎么返回的?

之前就把call指令的下一个指令的地址就压进栈了,把ebp调用这个函数的上一个函数栈帧的ebp就存进去了,当我们函数调用完要返回时候,弹出edp,就可以找到原始(上一个函数调用的ebp),指针往下走就可以找到esp的地址,回到了mian函数。

记住call指令的下一个指令地址就可以回来进行下一个命令。

返回值通过寄存器来临时保存,之后进行返回。

相关文章:

C语言(函数)—函数栈帧的创建和销毁

目录 前言 补充知识 一、函数线帧是什么&#xff1f; 二、函数线帧的实现&#xff08;举例说明&#xff09; 两数之和代码 ​编辑两数之和 汇编代码分析 执行第一条语句 执行第二条语句 执行第三条语句 执行第四、五、六条语句 执行第七条语句 执行第八、九、十条语句 执行第十…...

点餐小程序实战教程20广告管理

目录 1 创建数据源2 添加轮播容器3 创建变量4 绑定变量5 预览应用总结 一般餐厅需要有一些宣传&#xff0c;在我们的点餐页面可以在顶部加载广告位。广告主要是用轮播图的形式进行展示&#xff0c;本节我们介绍一下如果显示广告。 1 创建数据源 打开控制台&#xff0c;点击应用…...

市场上几个跨平台开发框架?

跨平台桌面应用开发框架是一种工具或框架&#xff0c;它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上&#xff0c;开发者需要为每个操作系统编写不同的代码&#xff0c;使用不同的开发工具和语言。而跨平台桌面应用开发框架通过…...

同步和异步、引用、变量声明、全局变量

同步和异步 如果计算机足够快&#xff0c;任何资源的访问速度都像Cache一样&#xff0c;没有异步的必要。 编程语言的同步和异步 越早期的编程语言&#xff0c;支持语言级别的异步越欠缺。 JS提供某些操作的同步和异步函数&#xff0c;例如文件读取&#xff0c;fs.readFile和fs…...

2024年10月份实时获取地图边界数据方法,省市区县街道多级联动【附实时geoJson数据下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…...

@RequestMapping对不同参数的接收方式

1、简单参数 1、参数名与形参变量名相同&#xff0c;定义形参即可接收参数&#xff0c;且会自动进行类型转换。 RequestMapping("/simple")public String simpleParam(String name,int age){String username name;int userAge age;System.out.println(username&…...

机器学习_KNN(K近邻)算法_FaceBook_Location案例(附数据集下载链接)

Facebook_location_KNN 流程分析: 1.数据集获取(大型数据怎么获取? 放在电脑哪里? 算力怎么搞?) 2.基本数据处理(数据选取-确定特征值和目标值-分割数据集) 缩小数据范围 选择时间特征 去掉签到较少的地方 确定特征值和目标值 分割数据集 3.特征工程(特征预处理:标…...

【str_replace替换导致的绕过】

双写绕过 随便输入一个 usernameadmin&passwords 没有回显测试注入点 usernameadmin or 11%23&passwords 回显hello admin测试列数 usernameadmin order by 3%23&passwords测试回显位 usernameadmi union select 1,2,3%23&passwords 没有显示数据&#xff0c;推…...

如何用AI大模型提升挖洞速度

工具背景 越权漏洞在黑盒测试、SRC挖掘中几乎是必测的一项&#xff0c;但手工逐个测试越权漏洞往往会耗费大量时间&#xff0c;而自动化工具又存在大量误报, 基于此产生了AutorizePro&#xff0c; 那它是怎么提升效率一起来看看 AutorizePro 是一款专注于越权检测的 Burp 插件…...

两个数列问题

# 问题描述 给定长度分别为 n 和 m 的两个数列a[n]、b[m]&#xff0c;和一个整数k。求|(a[i] - b[j])^2 - k^2|的最小值。 ## 输入格式 第一行有 2 个整数 n、m、k&#xff0c;分别表示数列 a、b 的长度&#xff0c;以及公式中的整数 k。 第二行有 n 个整数&#xff0c;表示…...

python中堆的用法

Python 堆&#xff08;Headp&#xff09; Python中堆是一种基于二叉树存储的数据结构。 主要应用场景&#xff1a; 对一个序列数据的操作基于排序的操作场景&#xff0c;例如序列数据基于最大值最小值进行的操作。 堆的数据结构&#xff1a; Python 中堆是一颗平衡二叉树&am…...

轮班管理新策略,提高效率与降低员工抱怨

良好轮班管理对企业关键&#xff0c;需提前计划、明确期望、保持灵活公平、加强沟通并利用轮班调度系统。ZohoPeople作为智能排班系统&#xff0c;提供轻松创建班次、自动更换、分配管理员、设置津贴及即时通知等功能&#xff0c;助力企业高效管理。 一、HR轮班管理的5大技巧 …...

spring-cloud-alibaba-nacos-config2023.0.1.*启动打印配置文件内容

**背景&#xff1a;**在开发测试过程中如果可以打印出配置文件的内容&#xff0c;方便确认配置是否准确&#xff1b;那么如何才可以打印出来呢&#xff1b; spring-cloud-alibaba-nacos-config 调整日志级别 logging:level:com.alibaba.cloud.nacos.configdata.NacosConfigD…...

数据结构:二叉树、堆

目录 一.树的概念 二、二叉树 1.二叉树的概念 2.特殊类型的二叉树 3.二叉树的性质 4.二叉树存储的结构 三、堆 1.堆的概念 2.堆的实现 Heap.h Heap.c 一.树的概念 注意&#xff0c;树的同一层中不能有关联&#xff0c;否侧就不是树了&#xff0c;就变成图了&#xff…...

hi3798mv100 linux 移植

# Linux开发环境搭建 ## uboot编译 1. 必须先安装gcc&#xff0c;要不然make 等命令无法使用 2. 配置arm 交叉编译链 # gcc sudo apt-get install gcc-9 gcc -v# 安装 Linaro gcc-arm-linux-gnueabihf&#xff0c;注意不是arm-linux-gnueabihf-gcc sudo apt-get install ar…...

Docker-Harbor概述及构建

文章目录 一、Docker Harbor概述1.Harbor的特性2.Harbor的构成 二、搭建本地私有仓库三、部署 Docker-Harbor 服务四、在其他客户端上传镜像五、维护管理Harbor 一、Docker Harbor概述 Harbor 是 VMware 公司开源的企业级 Docker Registry 项目&#xff0c;其目标是帮助用户迅…...

部署项目最新教程

​ 3.3安装mysql 运行代码&#xff1a; yum install mysql 运行代码&#xff1a; yum install mysql-server 中间还是一样要输入y然后回车 运行代码&#xff1a; yum install mysql-devel 好&#xff0c;经过上面三步&#xff0c;mysql安装成功&#xff0c;现在启动mysql…...

linux证明变量扩展在路径名扩展之前执行

题目&#xff1a;怎么设计一组命令来证明变量扩展在路径名扩展之前执行。 为了证明变量扩展在路径名扩展之前执行&#xff0c;可以通过编写一个简单的 shell 脚本来观察这两个过程的顺序。我们可以使用以下步骤进行设计&#xff1a; 步骤 1&#xff1a;准备环境 在你选择的 …...

CentOS 7.9安装MySQL

下载Linux版MySQL安装包 下载地址https://downloads.mysql.com/archives/community/ 下载解压后 安装&#xff0c;按照从上至下顺序&#xff0c;一条一条执行即可安装完毕。 进入到rpm所在目录rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm rpm -ivh mysql-comm…...

MacOS虚拟机安装Windows停滞在“让我们为你连接到网络”,如何解决?

1. 问题描述 MacOS在虚拟机安装win11过程中&#xff0c;停止在“让我们为你连接到网络”步骤&#xff0c;页面没有任何可以点击的按钮&#xff0c;进行下一步操作。 2. 解决方案&#xff08;亲测有效&#xff09; 到达该界面&#xff0c;按下ShiftF10&#xff08;Windows&…...

黑马程序员Java笔记整理(day03)

1.switch 2.for与while对比 3.嵌套定义,输出的区别性 4.break与continue 5.随机数生成的两种方式 6.Random 7.随机验证码...

centos7更换阿里云镜像源操作步骤及命令

centos7更换阿里云镜像源 在CentOS 7上更换为阿里云的镜像源可以通过以下步骤进行&#xff1a; 备份当前的YUM源配置文件 sudo cp -a /etc/yum.repos.d /etc/yum.repos.d.backup清理原有的YUM源配置文件 sudo rm -f /etc/yum.repos.d/*.repo下载阿里云的CentOS 7源配置文件 …...

冲刺大厂 | 一个线程调用两次start()方法会出现什么现象?

大家好&#xff0c;我是冰河~~ 今天给大家分享的面试题是&#xff1a;一个线程调用两次start()方法会出现什么现象&#xff1f;这道面试题是一道关于多线程的基础面试题&#xff0c;很多小伙伴对这个面试题不太了解&#xff0c;其实&#xff0c;如果你看过JDK中关于Thread类的…...

leaflet(一)初始化地图

Leaflet 与天地图结合使用&#xff0c;可以通过天地图提供的 API 获取地图瓦片&#xff0c;并在 Leaflet 地图上显示。 1. 安装依赖 首先&#xff0c;确保你已经安装了 Leaflet 和 Vue&#xff1a; npm install leaflet npm install vue-leaflet npm install leaflet.tilela…...

Unity开发Hololens项目

Unity打包Hololens设备 目录Visual Studio2019 / Visual Studio2022 远端部署设置Visual Studio2019 / Visual Studio2022 USB部署设置Hololens设备如何查找自身IPHololens设备门户Unity工程内的打包设置 目录 记录下自己做MR相关&#xff1a;Unity和HoloLens设备的历程。 Vi…...

立志最细,FreeRtos的中断管理(Interrupt Management)函数,详解!!!

前言&#xff1a;本文参考&#xff0c;韦东山老师开发文档&#xff0c;连接放在最后。 为什么需要中断管理函数&#xff1f; 在FreeRtos操作系统中&#xff0c;需要实时响应性&#xff0c;也就是随时随地必须保证正常多任务的运行&#xff0c;如果有中断发生&#xff0c;因为中…...

作业2-线性回归的Matlab代码实现

一、前言 相关配置&#xff1a;Matlab 2020a&#xff08;版本的影响应该不大&#xff0c;.m代码基本都能运行&#xff0c;个人感觉就是Simulink对版本的要求高一些&#xff09; 二、任务描述 基于近两节课的理论推导&#xff0c;用代码实现线性回归&#xff0c;并对预测结果进…...

用jQuery在canvas上绘制绝对定位的元素

在Web开发中,我们经常需要在canvas上精确定位和绘制元素。虽然canvas本身不支持DOM元素的定位,但我们可以借助jQuery来实现这一功能。本文将介绍如何使用jQuery在canvas上实现元素的绝对定位。 1. 基本思路 我们的基本思路是: 创建一个包含canvas的容器div将需要定位的元素放…...

Android中 tools:text 和 android:text区别

首先引入命名空间 <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"/androidx.constraintlayout.widget.ConstraintLayout> tools:te…...

Wordpress GutenKit 插件 远程文件写入致RCE漏洞复现(CVE-2024-9234)

0x01 产品简介 GutenKit 是一个WordPress的页面构建器,在 Gutenberg 设计您的下一个 WordPress 网站。借助 Gutenberg 的原生拖放界面、50+ WordPress 块、14+ 多功能模块和 500+ 模板,您可以在几分钟内创建专业、响应迅速的 Web 内容。 0x02 漏洞概述 Wordpress GutenKit…...