C语言—函数栈帧

news2024/11/26 8:25:37

        函数,一般都有返回值,函数名,参数,再下来还有什么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函数了,所以一次完整栈帧创建和释放

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何获取VS Code扩展的版本更新信息

获取VS Code 扩展的版本更新的需求 因为企业内部有架设私有扩展管理器的要求,但是对于一些官方市场的插件,希望可以自动获取这些扩展的更新并上传至私有扩展管理器。于是就有了本篇介绍的需求: 通过API的方式获取VS Code 扩展的更新。 关于…

Spring Boot集成sentinel快速入门Demo

1.什么是sentinel? 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、…

python从入门到精通:判断语句

目录 前言 1、布尔类型和比较运算符 2、if语句的基本格式 3、if else语句 4、if elif else语句 5、判断语句的嵌套 6、实战演练 前言 逻辑判断是生活中常见的行为。同样,在程序中,进行逻辑判断也是最为基础的功能。 判断是程序最基础最核心的逻辑…

远程桌面工具企业版:Splashtop Enterprise

在当今全球化和数字化的商业环境中,远程工作和分布式团队合作成为了新常态。企业需要一种高效、安全的远程桌面解决方案,能够满足不断变化的业务需求,同时确保数据的安全性和员工的生产力。Splashtop Enterprise 是一款为企业量身定制的远程桌…

haproxy负载均衡之-调度算法详解

HAProxy的调度算法分为静态调度算法、动态调度算法和其他调度算法静态算法:按照事先定义好的规则轮询公平调度,不关⼼后端服务器的当前负载、链接数和响应速度等,且⽆法实时修改权重,只能靠重启HAProxy⽣效。动态算法:…

【NI-DAQmx入门】LabVIEW数据采集基础应用程序框架

对于可管理规模的 LabVIEW 程序,分析现有程序或设计新程序的方法通常是从整体到具体,即从高级到低级的分析和设计。从一开始就直接深入细节可能会效率较低。 在设计阶段,开发人员首先将程序垂直划分为几个层级。从最顶层开始,他们…

强化学习之Actor-Critic算法(基于值函数和策略的结合)——以CartPole环境为例

0.简介 DQN算法作为基于值函数的方法代表,基于值函数的方法只学习一个价值函数。REINFORCE算法作为基于策略的方法代表,基于策略的方法只学习一个策略函数。Actor-Critic算法则结合了两种学习方法,其本质是基于策略的方法,因为其目…

element时间段选择器或时间选择器 只设置默认起始时间或者结束时间,不显示问题

element时间段选择器或时间选择器 只设置默认起始时间或者结束时间&#xff0c;不显示问题 <div v-for"(item,index) in [a,b]":key"item"><el-date-pickerv-if"b"v-model"value1[item]"type"datetimerange"value-…

16s功能注释Bugbase的安装使用--本地版

文章目录 概述介绍下载安装程序下载并配置环境安装依赖R包并显示帮助运行示例数据Bug及解决方法-☆ 使用输入文件准备-☆下载Greengenes数据库在QIIME2中操作R语言操作 运行Bugbase 概述 Bugbase依赖于Greegenes1与R 但是R现已更新到4.4以上&#xff0c;安装R包时会不兼容且输…

【时时三省】(C语言基础)结构体初阶

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 结构体的声明 结构的基础知识: 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量 数组: 是一组相同类型的元素的集合 结构体: 也是一些值得集合…

python-二进制?十进制?(赛氪OJ)

[题目描述] 给定两个十进制整数 : A&#xff0c;B。 你需要把它们的二进制形式以十进制的运算法则相加输出结果。 例如&#xff1a; A3 , B2的时候&#xff0c;A 的二进制表示是 : 11 , &#x1d435;B 的二进制表示是 10 &#xff0c;你需要输出答案为 : 21。 输入格式…

基于 Flutter 从零开发一款产品(一)—— 跨端开发技术介绍

前言 相信很多开发者在学习技术的过程中&#xff0c;常常会陷入一种误区当中&#xff0c;就是学了很多技术理论知识&#xff0c;但是仍做不出什么产品出来&#xff0c;往往学了很多干货&#xff0c;但是并无实际的用处。其实&#xff0c;不论是做什么&#xff0c;我们都需要从…

嵌入式linux系统镜像制作day1

点击上方"蓝字"关注我们 01、前言 嵌入式设备&#xff08;例如心电图检测仪&#xff0c;售票系统等&#xff09;。尽管&#xff0c;嵌入式设备像那些智能手机一样&#xff0c;绝大多数都使用同样的硬件和软件&#xff0c;包括系统芯片SoC、储存、连接和多媒体接口、…

Could not find artifact net.sf.json-lib:json-lib:jar

一开始我改了maven的setting&#xff0c;由官网变为阿里云仓库&#xff0c;最后还是不行 <dependency><groupId>net.sf.json-lib</groupId><artifactId>json-lib</artifactId><version>2.0</version><classifier>jdk15</cl…

freeRTOS任务通知(Task Notifications)

目录 前言 一、任务通知概述 1.优势及限制 2.通知状态和通知值 二、任务通知的使用 两类函数 1.xTaskNotifyGive/ulTaskNotifyTake 2.xTaskNotify/xTaskNotifyWait 三、传输计数值代码示例 四、传输任意值代码示例 前言 所谓"任务通知"&#xff0c;你可以反…

螺旋矩阵 | LeetCode-59 | LeetCode-54 | 分类讨论

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f383;分类不好&#xff0c;这道题就做不出来&#xff01;&#x1f388; &#x1f4cc;LeetCode链接&#xff1a;59. 螺旋矩阵 II &#x1f4cc;LeetCode链接…

李晨晨的嵌入式学习 DAY25

今天对昨天的fork函数进行了补充&#xff0c;并且学习了exec函数 一&#xff0c;fork函数补充 1.open在fork之前 子进程会继承父进程已打开的相关信息&#xff0c;父子进程会影响同一个offset值 2.open在fork之后 父子进程各自有各自打开的文件信息&#xff0c;不相互影响 …

Luatos-lua For MacOSX

0x00 缘起 看到Luatos-soc-pc项目能够编译到MacOS平台并且运行&#xff0c;所以尝试编译&#xff1b;可是Apple Clang编译器太过于严格&#xff0c;导致编译不通过。遂换到gcc-11编译通过&#xff0c;虽然其中依旧会报错&#xff08;宏定义LUA_USE_MACOSX不起作用&#xff0c;导…

Linux驱动入门实验班——LED驱动(附百问网视频链接)

目录 一、确定引脚编号 二、编写思路 2.1驱动层 2.2应用层 三、源码 四、实现 课程链接 一、确定引脚编号 首先&#xff0c;可以在开发板上执行如下命令查看已经在使用的GPIO状态&#xff1a; cat /sys/kernel/debug/gpio 可以看到每个gpio都有对应的编号&#xff0c;…

岗位信息采集全攻略:两种方法快速获取招聘信息

摘要 本文将揭秘两大实战策略&#xff0c;助你在激烈的人才市场中迅速捕捉前程无忧上的宝贵岗位信息&#xff0c;无论是手动搜索还是利用现代技术手段&#xff0c;都能事半功倍&#xff0c;抢占先机。 正文 一、手动搜索的艺术&#xff1a;精准定位&#xff0c;深度挖掘 1.…