C语言—函数栈帧

函数,一般都有返回值,函数名,参数,再下来还有什么mian函数,函数写出来就是要被调用的,上面图片上的代码,main函数和myadd函数,都要在自己的栈结构什么形成自己的栈,可以帮我们理解局部变量,为什么是临时,栈上开辟空间,栈的地址是向下增长,堆也学了,堆向上增长,每调一个函数,就是形成栈帧的一个过程,返回函数是栈帧被释放的一个过程,释放不是真正的释放,而是让这块空间无效,下一次创建栈帧的时候可以覆盖掉。
局部变量为什么具有临时性,是因为在对应的函数内栈帧上面创建的,函数被返回时,栈帧结构都被释放了,赖以生存的环境都没有了,所以变量也被释放了。
今天就来看看栈帧的形成与释放。
把一个函数的生命周期理解,其他都好理解了,因为从main函数开始都是函数调用,一个函数的生命周期搞定了,整个生命周期都搞定了。
我直接从myadd开始,就预设main函数有自己的栈帧了,只要myadd理解透了,其他的都搞定了。
先来了解一下东西寄存器和一些简单的汇编一下命令

寄存器最重要的就是后三个,ebp、esp,有了这两个寄存器,就可以有效指向一块内存,eip保存着当前指令的下一条指令的地址,本质就是衡量我们指向到了某个位置

先把代码和图画出来,画出内存分布图,我们栈帧结构主要研究栈区,所以把栈区放大,栈区地址是往小的增的。
代码启动调试之后一路f10,进来 ,看图片的右上角,出现了几个隐藏的函数,所以main函数也是一个函数。main函数是被谁调用的。
是被_tmianCRTStartup()调用的,但是它也是函数也谁调用它

它也是被 mainCRTStartup调用,只是做了一下cookie

mainCRTStartup它也是函数呀,它被谁调用?记住一件事,要被执行,第一步加载到内存,第二步就要开始执行,开始之前永远都是,操作系统来做,调mainCRTStartup,这里就是调用边界了。
所以我先研究add的栈帧,就是那个绿色的方格,只要一个清楚了其他的都清楚了,好,准备工作完成。

我重新画一张图,除了栈,还有cpu和三个寄存器,前面说了

esp和ebp指向main的起始位置和结束的位置,也就是地址,

eip先代表着执行者main的代码

好前期工作完成 ,启动反汇编,

直接来到这里 ,意思是esp-8个字节定位的位置吧0Ah,放到我们的栈帧当中,一条汇编做了两件事,开辟空间,完成初始化


看eip寄存器指向的位置,还没有开始执行,

程序从main开始执行,这一步完成之后,ebp-8就是栈底向上偏移8,所以x的位置就在这里,就是下面这样的,ebp-8是这块栈内存里面的一个偏移。x区域的起始地址,在上面位置因为内存排布是有低到高的 ,但是这里的内存是反着的,看地址排布,
起始地址是往上减少的,栈空间就往底地址增长的。
所以有一点要注意的是的起始地址是在上面
eip的是执行当前main函数的代码

然后下一条,[ebp-14h],ebp不变,减14,把y放到栈上

这一步完成之后,是这样的但是有一个注意的地方就是黑色的线是esp-14的位置,但是内存指向的话应该是指向最最小的内存,起始地址是从小的开始,所以y的起始地址,在上面位置因为内存排布是有低到高的 ,但是这里的内存是反着的,看地址排布,

下一步,f10,把0,放到ebp-20的这个位置,eip指向了1883的地址处,

执行完之后,然后z也入栈了

但是会发现一个问题就是y和z是靠着的,x和y是分开的,这是和编译器有关的。因为空间是随机的,所以也有可能是镂空的,应该是防止程序员猜测一些地址。
在接下来。到了,myadd的位置,可以发现一条C语言的指令,可能是有多条汇编指令

接下来,一个是eax是一个寄存器,还有一个是ebp-14

下面可以看到,ebp-14是y的地址,也是说要将y的里面的值放到eax的寄存器里面


来看一下,这里要把内存窗口打开,当我们按一下f10,到了push这里,push就要进栈,看一下esp的位置,也就是栈顶,

按一下f10,注意到esp的位置右8c变成了88,减少了4个字节,并且0xB已经在88位置,也发现了一个问题ebp的88已经变成新的栈了,B值已经压到了栈上了

所以,栈顶的位置,由内存图可以看到,是直接压在原来栈顶之上的位置,栈顶的位置也发生了改变,也压进来了。

相当于,把y的值拿到了寄存器里面,再从寄存器放到下一个位置。好像就是y的一份拷贝

下一步,这里和上面一样,需要一个寄存器ecx,ebp-8的位置是x的地址,也是把x的值放到ecx里面。

中途遇到了点问题,重启之后,地址变了。
中途遇到了点问题,重新了调试按一下f10,注意到esp的位置右c8变成了c4,减少了4个字节,也可以看一下ECX的寄存器值是00000A了,放进去寄存器里面了并且0xA已经在c4位置,也发现了一个问题ebp的c4已经变成新的栈了,A值已经压到了栈上了,

所以,栈顶的位置,由内存图可以看到,是直接压在原来栈顶之上的位置,栈顶的位置也发生了改变,也压进来了。 相当于,把X的值拿到了寄存器里面,再从寄存器放到下一个位置。好像就是X的一份拷贝
所以下面这四个动作,是做了一个临时拷贝

两个总结:
1.临时变量的形成是在函数正式被调用之前就形成了的
2.形参实例化的顺序是从右到左的
接着下来,到了call了,call的作用就是函数调用,1.压入返回值地址 2.转入目标函数

call调用函数,只需要修改eip的值就可以了,但是调完之后我还要返回,所以要不保存返回值地址,返回到call后面的add,为什么要压入返回值地址:根本原因就是函数掉完,之后需要返回。


看图片,解析,ESP会保存00f110f0de1这个跳转地址,并且把add的地址压栈,压到c0位置

按f11,发现返回值已经把栈的位置已经压入了,这里的地址压进去了,eip也变成了00f110f0,

这里要jmp, jmp就是跳转到函数,eip上的地址就是jmp前的地址,jmp后eip的地址就要变成00方18f0,

jmp后,eip就是00f118f0的地址就是,就是已经进来函数里面了,从现在就正式进来了,可以知道00f118f0就是函数的入口
完成后, eip位置就不再是mian了而是mydd了,已经进到myadd里面了

到这里,就已经完成了函数调用前的准备了, 形参列表初始化,形参列表完成,返回值入栈,eip跳转函数,怎么运行,下面准备开始myadd函数,接下来重点就是前三个。这三个就是栈帧最核心的内容。

第一步, push ebp是什么意思, ebp是什么?ebp不就是main函数的栈底吗,所以这里要做的就是把main函数的栈底入栈,

按一下f10,看到了把。已经是压进去了,

所以图就变成这个样子了,进去之后,esp的位置一定要改变,

第二步mov ebp,esp,意思就是把esp里面的内容,放到ebp里面去,esp原本是指向是main函数的栈顶,那如果直接放进去不会覆盖原来的吗,那到时候怎么找回来,不用担心,因为ebp的值在刚刚已经入栈了,

按f10 ,看到esp和ebp一样了,所以是是放进去了

所以,ebp不在执行main函数的栈底,所以栈顶和栈底都指向栈顶
sub是减的意思, 0CCh,意思是栈顶esp减去0CCh这个空间,然后把结构放到esp,0cch这里这里减多大,是和我自己当前的函数规有关系的,多变量就减的比较大,反之就少。

目前esp是00A2F794,按f10,就变成了00A2F6C84

所以esp减少了,是不是就指向了一段新的空间,ebp不变,变成如下这个样子

esp减去了一段范围,所以就变成了这个样子了 ,形成了myadd的栈区,esp和ebp也不是main的栈了
所以回到这张图,main有自己的栈帧,myadd有自己的栈帧
所以接下来直接到int c = 0这里,中间那些事完成一些初始化的问题,就不看了。
C是myadd里面定义的一个变量,和前面一样, 在ebp-8的地方把c放进去,ebp-8就是以ebp为参照物减去8个字节。

所以0就进来了
接下来,下一条指令,就是eax,dword ptr [ebp+8],是什么意思呢?
ebp-8就是以ebp为参考减去8个字节向上走,那加就是反着走,所以ebp+8就是刚刚找到a的值,并且放到eax里面,
重点:内存取出来的一定是连续地址最小的哪一个


add eax,dword ptr [ebp+0Ch],就是找到ebp+12的值和eax的想加,那这个位置是谁,刚好是b的值,eax刚好把a的值拿出来,最后两个相加,然后结果放回到eax
重点:内存取出来的一定是连续地址最小的哪一个

ea寄存器已经是15了,15是16进制,转回10进制就是21.

所以下面这一步是将eax的值写回到ebp-8的位置上,而前面ebp-8,就是c的位置

所以执行后是这个样子的


到此栈帧已经创建结束了,下一步就是return 返回了
经过前面分析知道函数栈帧是自己形成的,但是这里的sub减多少是谁决定的?答案是编译器决定的,只要你定义变量就要有类型,只要知道类型了编译器就知道给你开多大了,所以sizeof是在之前就计算好的了
开始return返回,如何理解return,放回值,都是通过eax和ecx等等这种通用这些寄存器返回的,

我只看这四条指令,中间那些pop只是配置一些寄存器的和上面的push只是一样的,重点是圈起来这四个

mov eax,dword ptr [ebp-8]这个很简单,就是把值放到了eax上面
下面三条才是重点
mov esp, ebp就是把ebp的值放到esp里面,ebp是myadd栈底,所以把myadd的栈底放到esp里面

相当于esp和ebp都是向了同一个地方,,所以相当于就是把myadd的空间给释放了,所以只需要让esp执行ebp的地方myadd的整个空间就释放了,
接下来就到了这个了 pop ebp 把当前栈顶的值弹出去,弹到ebp哪里去,
弹出去后,栈顶就指向了下一个地方了。

弹出后,栈顶就指向了返回值哪里,代表着什么

ebp指向原来的main栈底了

ret就是将返回值地址返回到eip,这个返回值地址就是但是call的下一条指令地址,因为和pop一样,所以栈顶也要退回下一个所以变成这个样子

下图可以发现ret之后栈顶是变大了
可以分享已经返回到了这里
打×的地方就是栈已经释放了 
那还有两个怎么办?可以这里 add esp,8,意思是说栈顶的位置向下加8个字节

就这样就把两个空间给释放了
所以这就回到了main栈帧,main函数了,所以一次完整栈帧创建和释放
相关文章:
C语言—函数栈帧
函数,一般都有返回值,函数名,参数,再下来还有什么mian函数,函数写出来就是要被调用的,上面图片上的代码,main函数和myadd函数,都要在自己的栈结构什么形成自己的栈,可以帮…...
IDEA 2022.1.4用前需知
目录 一、配置国内源 二、正确再次创建新项目方式 IDEA 2022.1.4下载地址 一、配置国内源 1、查看本地仓库地址 2、设置国内源-添加Setting.xml文件内容 3、修改目录(考虑到当前硬盘空间大小,英文目录名) 1)创建你要移动过去…...
Python数据可视化案例——折线图
目录 json介绍: Pyecharts介绍 安装pyecharts包 构建一个基础的折线图 配置全局配置项 综合案例: 使用工具对数据进行查看 : 数据处理 json介绍: json是一种轻量级的数据交互格式,采用完全独立于编程语言的文…...
Ubuntu虚拟机安装及汉化
一、安装 1.勾选典型(推荐)(T)——点击下一步 2.点击浏览找到光盘映像文件打开(此文件很重要安装好后安装包不要卸载,放在不容易被删除的地方)——点击下一步 3.将信息补充完整——点击下一步 4.点击浏览选择要将虚拟机安装在哪个路径&…...
记2024-08原生微信小程序开发
继2024.08 最近需要开发一个微信小程序的一个功能模块,但是之前在学的时候都是好几年前的东东了,然后重新快速过了一遍b站大学的教程,这篇文章就是基于教程进行的一些总结,和自己开发过程当中使用到的一些点和一些技巧什么的吧。 …...
嵌入式linux系统镜像制作day1
点击上方"蓝字"关注我们 01、前言 嵌入式设备(例如心电图检测仪,售票系统等)。尽管,嵌入式设备像那些智能手机一样,绝大多数都使用同样的硬件和软件,包括系统芯片SoC、储存、连接和多媒体接口、…...
【相机与图像】2. 相机内外参的标定的代码示例
1 摄像头内参的标定 【相机标定具体操作】 使用将要标定的摄像头,以不同的角度采集棋盘格,要保证视野内出现完整的棋盘格。采集图片数量约15张左右即可。 以11*8的棋盘格为例,具体流程如下: step 1. 设置棋盘格3D点;通…...
重启人生计划-拒绝内耗
🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳 如果你觉得这个【重启人生…...
盘点电脑开机慢的几大高频原因
常规的话一台电脑正常我们都要用个2年以上的时间,有的可能更长,5年的都有,而电脑目前占多数的主流操作系统就是微软的Windows。那么随着使用年限的增加,无论是系统还是电脑硬件,都会随着使用次数和使用的时间的增加而有损耗,系统软件上就是文件越来越臃肿,空间越来越小,…...
2-64 基于matlab的Consensus-Based Bundle Algorithm (CBBA)算法
基于matlab的Consensus-Based Bundle Algorithm (CBBA)算法,可为异构代理网络上的多代理多任务分配问题提供良好的解决方案。支持具有有效时间窗口的任务、异构代理-任务兼容性要求,以及平衡任务奖励和燃料成本的得分函数。奖励和燃料成本的分数函数。程…...
Win10 去掉桌面右上角 了解有关此图片的信息
1. 进入注册表 Win R运行regedit 2. 找到以下路径 计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel 3. 新建 DWORD(32位)值(D) 右击 NewStartPanel新建 DWORD…...
tcpdump入门——抓取三次握手数据包
1. 使用docker启动一个tcp应用 参考:https://blog.csdn.net/LONG_Yi_1994/article/details/141175526 2. 获取容器id docker ps |grep gochat 3. 获取容器的 PID 首先,你需要获得容器的进程 ID(PID)。可以使用 docker inspect…...
漏洞复现-GitLab任意读取文件(CVE-2023-2825)
1.漏洞描述 GitLab是一个用于仓库管理系统的开源项目,其使用Git作为代码管理工具,可通过Web界面访问公开或私人项目。据悉,该漏洞影响 GitLab社区版(CE)和企业版(EE)的 16.0.0 版本,其它更早的版本几乎都不受影响。 该漏洞存在于GitLab CE/EE版本16.0.0…...
二叉树——9.找树左下角的值
力扣题目链接 给定一个二叉树,在树的最后一行找到最左边的值。 示例: 输出:7 题干很简单,找到树的最后一行,在该行找到最左边的值,结合完整代码进行分析。 完整代码如下: class Solution:d…...
如何用github制作个人网站
这里整理了一些参考资料。总结来说,如果系统学过html网页制作的话,可以不用看这篇博客了;这里适合于小白,就是那种 没有做过网页、打算以别人优秀的个人主页为框架做网页的小白。 一、简单说明 这是利用github.io来制作网页的&a…...
二.PhotoKit - 相册权限(彻底读懂权限管理)
引言 用户的照片和视频算是用户最私密的数据之一,由于内置的隐私保护功能,APP只有在用户明确授权的前提下才能访问用户的照片库。从iOS14 开始,PhotoKit进一步增强了用户的隐私控制,用户可以选择指定的照片或者视频资源的访问权限…...
二叉树------最小堆,最大堆。
什么是最小堆: 堆是一种二叉树,最小堆中所有父亲节点的值都要比自己的子节点的值要小。而根节点称为堆顶。根据定义我们可以得到堆中最小元素就在堆顶。(节点左上角是编号,内部是元素值) 假设该图中的堆顶元素是24呢&a…...
预约功能的知识整理
前置知识 如果项目为小程序的开发项目中: 我们确定数据库中有的字段有: 预约人姓名、手机号、家人名称、预约时间 根据我们的经定一表必须要有的6个字段: 主键、创建时间、修改时间、创建人、修改人、备注 使用我们现在有的字段为: 主键…...
Linux的常用操作-02
一:Linux的系统目录结构 /bin bin是ary的缩写,这个目录存放着最经常用的命令 /boot:这里存放的是启动Linux时使用的一些核心文件,包括一些连接文件以及镜像文件。 /dev:dev是Device(设备)的缩写,该目录下存放的是Lin…...
Android Studio 连接手机进行调试
总所周知,Android Studio里的虚拟手机下载后又大又难用。不如直接连手机用。本篇文章主要内容为Android Studio怎么连接手机进行程序调试。 1. 在AndroidSDK中下载google USB Driver: 2. 连接手机: 进入电脑设备管理器界面。并点开便携设备,…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
【服务器压力测试】本地PC电脑作为服务器运行时出现卡顿和资源紧张(Windows/Linux)
要让本地PC电脑作为服务器运行时出现卡顿和资源紧张的情况,可以通过以下几种方式模拟或触发: 1. 增加CPU负载 运行大量计算密集型任务,例如: 使用多线程循环执行复杂计算(如数学运算、加密解密等)。运行图…...
