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

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临时保存,这样值也会被寄存器带回。

 

相关文章:

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

目录 如果你能熟练的掌握函数的栈帧与销毁在面试中是及其亮眼的加分项,所以我们来以实例来将解函数是如何实现栈帧与销毁的。 一. 函数栈帧 二.寄存器 三. 用例题讲解创建栈帧的过程 3.1 main 函数的反汇编代码。 第一步:给调用main函数的函数分配…...

使用 GreenSock(GSAP)实现 字符串动画

要使用 GreenSock(GSAP)实现 "JianMa XinXi" 这个字符串的动画,其中两个 x 字符自动旋转,j 和 m 字符上下跳动,并且美化这个字符串使其可以作为 logo 使用,我们可以通过以下步骤来实现&#xff1…...

linux系统zabbix监控服务端部署

zabbix服务端部署 zabbix服务端部署安装mysql创建初始数据库为Zabbix server配置数据库为Zabbix前端配置PHP启动Zabbix server和agent进程浏览器访问ipConfigure DB connection页面Zabbix server details页面登录账户名密码 zabbix 官网www.zabbix.com服务端部署 rpm -Uvh ht…...

算法----回溯(附录---剪枝)

回溯相信大家都已经了解了所以这章我将见但介绍下回溯剪枝 为什要剪枝 在《算法----回溯(正文)》中我提到过回溯就是暴力,为什么那些题能过,因为数据范围小 那如果数据范围大了,就不行了,这时剪枝的作用就…...

从Unity到Three.js(模型文件加载)

模型加载功能探索,用blender导出了个glb格式的cube进行的测试。 初接触js语法,回调注册的地方直接使用匿名函数总感觉脑子跟不上,反应不过来,就把加载后的回调简单封装了下, 官方文档是直接使用的匿名函数。 另外看官方…...

Webshell一句话木马

一、webshell介绍(网页木马) 分类: 大马:体积大、隐蔽性差、功能多 小马:体积小,隐蔽强,功能少 一句话木马:代码简短,灵活多样 二、一句话木马: :…...

【Web】Spring rce CVE-2022-22965漏洞复现学习笔记

目录 原理概览 漏洞简述 Tomcat AccessLogValve 和 access_log 例题: 原理概览 spring框架在传参的时候会与对应实体类自动参数绑定,通过“.”还可以访问对应实体类的引用类型变量。使用getClass方法,通过反射机制最终获取tomcat的日志配置成员属性…...

springboot/ssm大学生选修选课系统高校选课排课成绩管理系统Java系统

springboot/ssm大学生选修选课系统高校选课排课成绩管理系统Java系统 开发语言:Java 框架:springboot(可改ssm) vue JDK版本:JDK1.8(或11) 服务器:tomcat 数据库:my…...

【芯片设计- RTL 数字逻辑设计入门 14 -- 使用子模块实现三输入数的大小比较】

文章目录 三输入数的大小比较问题分析verilog codeTestBench Code综合图仿真波形图 三输入数的大小比较 在数字芯片设计中,通常把完成特定功能且相对独立的代码编写成子模块,在需要的时候再在主模块中例化使用,以提高代码的可复用性和设计的层…...

Xilinx FPGA——在线升级

同以前单片机在线升级的做法一样,本质就是通信Flash操作跳转。 一、通信驱动 我使用的是UDP有线传输, 二、Flash芯片驱动 规划Flash芯片的区域,一般bootloader放在起始位置,APP放在bootloader之后的空白区域。 2.1 Flash擦除 我…...

电商小程序02数据源设计

上一篇我们讲解了电商小程序的需求分析,分析了需要具备的功能并且绘制了系统原型。有了原型之后下一步的事情就是根据原型来设计数据源。 数据源就像盖房子打地基一样,地基打不好,楼可能就盖不高,盖起来要再想调整就比较困难。 …...

Leetcode 3033. Modify the Matrix

Leetcode 3033. Modify the Matrix 1. 解题思路2. 代码实现 题目链接:3033. Modify the Matrix 1. 解题思路 这一题是一道easy的题目,整体思路上没啥难度,就是按照题目翻译一下即可,先遍历一下找到每一列的最大元素&#xff0c…...

蓝桥杯刷题--python-4

0成绩分析 - 蓝桥云课 (lanqiao.cn) import os import sys # 请在此输入您的代码 n=int(input()) max_=float(-inf) min_=float(inf) res=0 for _ in range(n): score=int(input()) # 最高分 max_=max(max_,score) # 最低分 min_=min(min_,score) # 总分 res+=sc…...

openJudge | 距离排序

总时间限制: 1000ms 内存限制: 65536kB 描述 给出三维空间中的n个点(不超过10个),求出n个点两两之间的距离,并按距离由大到小依次输出两个点的坐标及它们之间的距离。 输入 输入包括两行,第一行包含一个整数n表示点的个数,第二…...

【算法】排序详解(快速排序,堆排序,归并排序,插入排序,希尔排序,选择排序,冒泡排序)

目录 排序的概念: 排序算法的实现: 插入排序: 希尔排序: 选择排序: 堆排序: 冒泡排序: 快速排序: 快速排序的基本框架: 1.Hoare法 2. 挖坑法 3.前后指针法 快…...

LeetCode Python -8.字符串转整数

文章目录 题目答案运行结果 题目 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C 中的 atoi 函数)。 函数 myAtoi(string s) 的算法如下: 读入字符串并丢弃无用的前导空格检查下一个…...

【java】笔记10:类与对象——本章练习

题目1: 代码如下: import java.util.Scanner; public class Input{public static void main(String[]args){Circle cnew Circle();PassObject yuannew PassObject();System.out.println("r""\t""times");yuan.printAreas…...

《UE5_C++多人TPS完整教程》学习笔记8 ——《P9 访问 Steam(Acessing Steam)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P9 访问 Steam(Acessing Steam)》 的学习笔记,该系列教学视频为 Udemy 课程 《Unreal Engine 5 C Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者&…...

缓存穿透问题与解决方案

引言 在分布式系统中,缓存技术被广泛应用以提高系统性能和响应速度。然而,缓存穿透是一个常见而严重的问题,特别是在面对大规模请求时。本文将深入探讨缓存穿透的原因、影响以及一些有效的解决方案,以确保系统在面对这一问…...

《Git 简易速速上手小册》第1章:Git 基础(2024 最新版)

文章目录 1.1 Git 简介:版本控制的演变1.1.1 基础知识讲解1.1.2 重点案例:协作开发流程优化案例:功能开发与分支策略 1.1.3 拓展案例 1:代码审查与合并1.1.4 拓展案例 2:冲突解决 1.2 安装和配置 Git:首次设…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案

随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

Map相关知识

数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...

C++使用 new 来创建动态数组

问题: 不能使用变量定义数组大小 原因: 这是因为数组在内存中是连续存储的,编译器需要在编译阶段就确定数组的大小,以便正确地分配内存空间。如果允许使用变量来定义数组的大小,那么编译器就无法在编译时确定数组的大…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

第7篇:中间件全链路监控与 SQL 性能分析实践

7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...