C语言函数的栈帧与销毁(面试亮点)

目录

如果你能熟练的掌握函数的栈帧与销毁在面试中是及其亮眼的加分项,所以我们来以实例来将解函数是如何实现栈帧与销毁的。

一. 函数栈帧

二.寄存器

三. 用例题讲解创建栈帧的过程

3.1 main 函数的反汇编代码。

第一步:给调用main函数的函数分配栈帧。 

第二步:通过将edp压栈,将edp赋值给esp,再将esp的值减减。这样esp和edp之间的差值就是main函数的栈帧空间。 

第三步: 将edi,esi,ebx压栈。

第四步:初始化

 第五步:给实参分配空间

第六步:将实参拷贝到新地址

第七步: call指令传参

call跳add()函数的地址之后,在添加call指令的下一条指令的地址。

3.2 add函数的栈帧 

第一步:为add创建栈帧

第二步:将形参相加:

3.3 return  的栈帧

第一步

z出了add()栈帧被销毁,但是值被临时放到eax寄存器里。

第二步

第三步:回收空间

第四步:pop edp 

第五步:回来之后执行call指令的下一条指令 

第六步 

创建的完整过程(没带销毁)

四.面试问题

4.1 局部变量时怎么创建的?

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

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

4.4 形参和实参的关系是什么?

4.5 函数的调用是怎么做的?

5.6 函数调用是结束后怎么返回的?


如果你能熟练的掌握函数的栈帧与销毁在面试中是及其亮眼的加分项,所以我们来以实例来将解函数是如何实现栈帧与销毁的。

一. 函数栈帧

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

每一个函数调用都要在栈区中创建一个空间。

二.寄存器

1.1 edp:栈底指针

1.2 esp:栈顶指针,每次push,它都会向上(低地址)挪动,push就会--,pop就会++。

esp和edp中间的 内容才是当前维护的内容,esp上方的内容已经销毁的内容

 寄存器:寄存器是集成到CPU 上的是独立的存储空间。与硬盘,内存都是独立存在的。

三. 用例题讲解创建栈帧的过程

 

3.1 main 函数的反汇编代码。

main函数也是被调用,main函数是被t_main CRTStartup调用。

第一步:给调用main函数的函数分配栈帧。 

第二步:通过将edp压栈,将edp赋值给esp,再将esp的值减减。这样esp和edp之间的差值就是main函数的栈帧空间。 

 

第三步: 将edi,esi,ebx压栈。

lea:是load effective address 是加载有效地址的意思。即esp-014H。

 

第四步:初始化

从edi开始向下ecx次即39h次,将ecx里dword(一个word两个字节,doubleword是四个字节)个内容初始化为0cccccccch,初始化39h(39h是十六进制,换成十进制次)次。即将main函数里的内容全部初始化为0cccccccch。 

所以如果变量不初始化,那么就默认0cccccccch。

 

 

 第五步:给实参分配空间

将ebp-14h(即b的值)存放到eax里,然后将eax压栈 。

  


 

第六步:将实参拷贝到新地址

不用给形参分配新空间,在函数调用之前,就将实参拷贝到新空间。

 

第七步: call指令传参

call的地址是

然后call指令,call指令跳到add()函数的地址

call跳add()函数的地址之后,在添加call指令的下一条指令的地址。

所以call()指令之后,将call指令的下一条指令的地址压栈。

 

 

3.2 add函数的栈帧 

然后才是真正的进入add()函数。 

 

第一步:为add创建栈帧

所以我们并没有创建形参,而是在调用函数的时候就把实参传递过去了。 将他们当成X,Y。

因此可以证明形参只是实参的拷贝。所以改变形参不会改变实参。

 

第二步:将形参相加:

 

 

3.3 return  的栈帧

形参是实参的临时拷贝,不是在add栈帧里创建的只是调用一个压栈的空间。 

第一步

  

z出了add()栈帧被销毁,但是值被临时放到eax寄存器里。

所以为了防止z出了作用域被销毁,所以将z放到eax寄存器里临时保存,寄存器是不会因为程序销毁而销毁的。 

第二步

 

pop会导致esp++ ,esp上面的栈会被释放被回收了S。

 

第三步:回收空间

 将ebp赋值给esp的意思就是将esp挪动到edp的位置,上面的空间被回收。

 

第四步:pop edp 

pop edp,将edp出栈,这里栈顶元素edp是main函数的edp,所以将他pop之后他会回到原main函数的edp位置。

然后esp++;这是esp和edp两个指针彻底回到main函数的作用域里。esp指向00C21450即回到了call指令的下一条指令的地址,所以要执行call指令的下一条指令。

第五步:回来之后执行call指令的下一条指令 

 

 

  

将esp+8:这里加8的目的值将两个形参的空间给销毁。

 

 

第六步 

 

 将eax的值赋值给edp -20h,刚刚我们为了防止z的值丢失所以我们将它的值存放在寄存器eax里,所以函数的任务完成了,现在需要将操作完的值还给main函数。

正好edp-20h就是main函数的C。这样返回值就被传回来了。

 

 

创建的完整过程(没带销毁)

四.面试问题

4.1 局部变量时怎么创建的?

        答:首先,我们为这个局部变量分配栈帧空间,然后在为这个空间初始化空间,然后在为局部变量分配空间。

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

        答:因为如果局部变量不初始化的话,函数栈帧空间的初始化是随机值,所以如果局部变量不初始化的话,那么它的值就是它分配的那段空间的随机值。如果你将局部变量初始化了,那么你初始化的值就会覆盖掉随机值。

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

        我们并没有创建形参,而是在调用函数的时候就把实参传递过去了。在还没有调用函数的时候,就以及将实参的拷贝push压栈到空间里,到调用函数的时候,并没有为形参分配空间,而是使用esp+偏移量,找到实参的拷贝。

        传参的顺序:参数从右向左压栈。

4.4 形参和实参的关系是什么?

        答:形参是实参的拷贝,改变形参不会影响实参。因为他们的空间是独立的。

4.5 函数的调用是怎么做的?

5.6 函数调用是结束后怎么返回的?

        在函数调用之前就将call指令的下一条指令的地址就压栈了,然后再将edp(此时的edp是调用函数的上一条函数如main函数的edp)压栈,这样在pop掉edp时,edp会返回到原edp的位置,由于pop操作,会使esp++,这样esp就会调用call指令的下一条指令的地址,这样就会时函数调用返回,然后返回的值,事先已被寄存器eax临时保存,这样值也会被寄存器带回。

 

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

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

相关文章

使用 Elasticsearch 和 OpenAI 构建生成式 AI 应用程序

本笔记本演示了如何: 将 OpenAI Wikipedia 向量数据集索引到 Elasticsearch 中使用 Streamlit 构建一个简单的 Gen AI 应用程序,该应用程序使用 Elasticsearch 检索上下文并使用 OpenAI 制定答案 安装 安装 Elasticsearch 及 Kibana 如果你还没有安装好…

Linux死机排查方法——内存日志

一般情况下,Linux系统在死机时会产生一些dump信息,例如oops,通过分析oops信息就可以基本定位问题所在,但有些特殊情况下死机时,没有任何的打印的信息。如果直接使用printk等打印排查问题,有可能会因为print…

生成式人工智能攻击的一年:2024

趋势科技最近公布了其关于预期最危险威胁的年度研究数据。生成人工智能的广泛可用性和质量将是网络钓鱼攻击和策略发生巨大变化的主要原因。 趋势科技宣布推出“关键可扩展性”,这是著名年度研究的新版本,该研究分析了安全形势并提出了全年将肆虐的网络…

以管理员权限删除某文件夹

到开始菜单中找到—命令提示符—右击以管理员运行 使用:del /f /s /q “文件夹位置” 例:del /f /s /q "C:\Program Files (x86)\my_code\.git"

动态SQl简单创建

创建pojo实体类,使用lombok注解 package com.example.pojo;import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.time.LocalDate; import java.time.LocalDateTime;Data NoArgsConstructor AllArgsConstructor pu…

记:STM32F4参考手册-存储器和总线架构

STM32F4参考手册-存储器和总线架构 系统架构 主系统由32位多层AHB总线矩阵构成,可实现以下部分部分的互连: 八条主控总线: Cortex-M4F内核I总线、D总线和S总线 DMA1存储器总线 DMA2存储器总线 DMA2外设总线 以太网DMA总线 USB OTG HS DMA总线…

秒杀相关问题解决

秒杀 超卖问题 如下,我们先来复现问题,抢购秒杀券的代码逻辑也是很简单, 先判断优惠券是否开始了,是的化,判断库存是否充足,如果是的化,扣减库存,最后创建订单 如下是代码 Override Transactional public Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVo…

力扣刷题之旅:进阶篇(六)—— 图论与最短路径问题

力扣(LeetCode)是一个在线编程平台,主要用于帮助程序员提升算法和数据结构方面的能力。以下是一些力扣上的入门题目,以及它们的解题代码。 --点击进入刷题地址 引言 在算法的广阔天地中,图论是一个非常重要的领域。…

linux 07 存储管理

02. ext4是一种索引文件系统 上面是索引节点inode,存放数据的元数据 下面是存储块block,主要存放有关的信息 03.linux上的inode 查看文件中的inode ll -i 文件名 磁盘中的inode与文件数量 向sdb2中写文件: 结果: df -i 磁…

blender几何节点中样条线参数中的系数(factor)是个什么概念?

一根样条线,通常由两个及以上的控制点构成。 每个控制点的系数,其实相当于该点处位于整个样条线的比值。 如图,一根样条线有十一个控制点。相当于把它分成了十段,那每一段可以看到x、y都是0,唯独z每次增加0.1&#xff…

JVM-双亲委派机制

双亲委派机制定义 双亲委派机制指的是:当一个类加载器接收到加载类的任务时,会自底向上查找是否加载过, 再由顶向下进行加载。 详细流程 每个类加载器都有一个父类加载器。父类加载器的关系如下,启动类加载器没有父类加载器&am…

NIS服务器搭建(管理账户密码验证)

理解:新进100台服务器,通过nis服务器设置各个服务器的用户和密码,而不是分别到100台机器前设置用户名密码,服务器可以统一管理用户名密码,更新等操作 第一:服务器端设置 1.域名设置:dongfang …

MyBatis 实现动态 SQL

MyBatis 中的动态 SQL 就是SQL语句可以根据不同的情况情况来拼接不同的sql。 本文会介绍 xml 和 注解 两种方式的动态SQL实现方式。 XML的实现方式 先创建一个数据表,SQL代码如下: DROP TABLE IF EXISTS userinfo; CREATE TABLE userinfo (id int(1…

二维差分---三维差分算法笔记

文章目录 一.二维差分构造差分二维数组二维差分算法状态dp求b[i][j]数组的二维前缀和图解 二.三维前缀和与差分三维前缀和图解:三维差分核心公式图解:模板题 一.二维差分 给定一个原二维数组a[i][j],若要给a[i][j]中以(x1,y1)和(x2,y2)为对角线的子矩阵中每个数都加上一个常数…

代码随想录|Day 14

Day 14 新年将至 一、理论学习 BFS 的使用场景总结:层序遍历、最短路径问题(https://leetcode.cn/problems/binary-tree-level-order-traversal/solutions/244853/bfs-de-shi-yong-chang-jing-zong-jie-ceng-xu-bian-l/) BFS 的应用一:层序遍历 BFS …

开发JSP应用程序

开发JSP应用程序 问题陈述 TecknoSoft Pvt Ltd.公司的首席技术官(CTO)John Barrett将创建一个应用程序的任务委托给了开发团队,该应用程序应在客户访问其账户详细信息前验证其客户ID和密码。客户ID应是数字形式。John希望如果所输入的客户ID或密码不正确,应向客户显示错误…

面试经典150题 -- 栈(总结)

总的链接 面试经典 150 题 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台 关于栈 -- stack 的学习链接 c的STL中的栈 -- stack-CSDN博客 20 . 有效的括号 这题直接用栈模拟就好了; 这里用一种取巧的方法 , 当遇见左括号,加入右…

MATLAB环境下基于同态滤波方法的医学图像增强

目前图像增强技术主要分为基于空间域和基于频率域两大方面,基于空间域图像增强的方法包括了直方图均衡化方法和 Retinex 方法等,基于频率域的方法包括同态滤波方法。其中直方图均衡化方法只是根据图像的灰度概率分布函数进行简单的全局拉伸,没…

containerd中文翻译系列(十九)cri插件

cri插件包含的内容比较多,阅读之前请深呼吸三次、三次、三次。 CRI 插件的架构 本小节介绍了 containerd 的 cri 插件的架构。 该插件是 Kubernetes 容器运行时接口(CRI) 的实现。Containerd与Kubelet在同一个节点上运行。containerd内部的…

修改SpringBoot中默认依赖版本

例如SpringBoot2.7.2中ElasticSearch版本是7.17.4 我希望把它变成7.6.1