函数战争(栈帧)之创建与销毁(c语言)(vs2022)


 首先,什么是函数栈帧?

        C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。栈帧也叫过程活动记录,是编译器用来实现过程函数调用的一种数据结构。

以问答的方式解释编译器与解释器-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/forccct/article/details/135349101?spm=1001.2014.3001.5501

函数栈帧的作用

        函数栈帧是编译器用来实现函数调用的一种数据结构。在执行函数时,每个函数都会分配一个独立的栈帧,用于存储该函数的参数、局部变量、返回地址等信息。

        栈帧的作用在于保存函数的运行环境,使得函数执行时可以随时访问其所需的参数和局部变量。当函数被调用时,其栈帧被推入栈中,成为当前活动的栈帧。当函数执行完毕后,其栈帧从栈中弹出,并释放相关的内存空间。

         在C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。通过寄存器ebp和esp来维护当前的栈帧。

     函数栈帧是编译器用来实现函数执行环境的一种数据结构,它保存了函数的参数、局部变量和返回地址等信息,使得函数可以正确地执行并保持其运行环境。

学习函数栈帧的目的

       学习函数栈帧的目的在于深入理解程序的执行机制和编译器的工作原理。通过了解函数栈帧,程序员可以更好地理解函数调用时的内存布局、参数传递、局部变量管理以及异常处理等方面的知识。

        此外,理解函数栈帧也有助于提高程序的性能和可维护性。例如,通过合理使用栈帧,可以避免不必要的内存分配和释放操作,提高程序的执行效率。同时,了解栈帧也有助于在调试和优化程序时更好地分析程序的运行状态和性能瓶颈。

        学习函数栈帧可以帮助程序员更好地理解程序执行过程和编译器的工作原理,提高程序的性能和可维护性,并为解决复杂问题提供更有效的解决方案。

函数栈帧可以说是编程者的”内功“,修炼内功能更好的去理解和学习语


基础知识(相关的)

寄存器 

需要用到的寄存器的名字和功能
eaxEAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器
ebxEBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址
ecx是计数器(counter), 是重复(rep)前缀指令和LOOP指令的内定计数器
edx则总是被用来放整数除法产生的余数
ebp寄存器存放当前线程的栈底指针
esp

寄存器存放当前线程的栈顶指针

  1. EAX (Accumulator Register):
    • 通常用于算术和逻辑运算。
    • 在函数调用中,它经常用来返回结果。
    • 在一些系统调用和指令中,eax 也用来传递参数。
  2. EBX (Base Register):
    • 通常用作指向数据的基址指针。
    • 在某些系统调用和指令中,ebx 也用来传递参数。
    • 在某些情况下,它也可以用作通用寄存器。
  3. ECX (Count Register):
    • 通常用作计数器,特别是在循环和字符串操作中。
    • 在某些系统调用和指令中,ecx 也用来传递参数。
  4. EDX (Data Register):
    • 通常与eax一起用于32位乘法和除法运算。
    • 在某些系统调用和指令中,edx 也用来传递参数或返回额外的结果信息。
  5. EBP (Base Pointer):
    • 在函数调用中,它通常用来指向当前函数的栈帧基址。
    • 通过ebp,函数可以方便地访问其参数和局部变量,即使栈顶指针(esp)在函数执行过程中发生变化。
  6. ESP (Stack Pointer):
    • 指向栈顶的指针。
    • 当数据被压入栈时,esp减小;当数据从栈中弹出时,esp增加(在x86体系结构中,栈是向下增长的)。
    • 通过修改esp,函数可以分配和释放栈空间。

     详细见:寄存器的相关知识-CSDN博客”icon-default.png?t=N7T8https://blog.csdn.net/forccct/article/details/135316297?spm=1001.2014.3001.5501

汇编命令

汇编语言是一种低级语言,它直接与计算机的硬件和操作系统交互。汇编命令(或指令)是汇编语言中的基本单位,用于控制计算机执行特定的操作。以下是一些常见的汇编命令:

  1. 数据传送指令
    • MOV:将数据从一个位置移动到另一个位置。
    • PUSH:将数据压入栈中,同时更新栈顶指针。
    • POP:从栈顶弹出数据,同时更新栈顶指针。
  2. 算术运算指令
    • ADD:将两个数相加。
    • SUB:从第一个数中减去第二个数。
    • MUL 和 IMUL:无符号乘法和有符号乘法。
    • DIV 和 IDIV:无符号除法和有符号除法。
  3. 逻辑运算指令
    • AND:按位与操作。
    • OR:按位或操作。
    • XOR:按位异或操作。
    • NOT:按位非操作。
  4. 控制流指令
    • JMP:无条件跳转到一个指定的地址。
    • Jcc(如 JZJNZJEJNE 等):基于某个条件(如零标志位、符号标志位等)进行跳转。
    • CALL:调用一个子程序,保存返回地址。
    • RET:从子程序返回,恢复返回地址。
  5. 比较指令
    • CMP:比较两个操作数,设置相应的标志位。
  6. 堆栈操作指令(除了 PUSH 和 POP):
    • PUSHF 和 POPF:将标志寄存器压入栈中或从栈中弹出。
    • ENTER 和 LEAVE:用于高级语言过程/函数的栈帧设置和清除。
  7. 输入输出指令(与硬件或操作系统交互):
    • IN 和 OUT:从端口读取数据或向端口写入数据。
  8. 其他指令
    • NOP:无操作,通常用于填充或微调代码时序。
    • HLT:停止执行并等待外部中断。
    • CLI 和 STI:清除或设置中断标志。
    • 等等。

需要注意的是,具体的指令集依赖于特定的处理器架构(如 x86, ARM, MIPS 等),不同的架构有不同的指令集和寻址模式。上述指令主要基于 x86 架构,其他架构的指令可能会有所不同。

我们主要用到了

mov     push     pop     sub     add      call       jump      ret

栈和栈帧

            堆栈(stack)又称为堆叠,是计算机科学里最重要且最基础的数据结构之一,它按照FILO(First In Last Out,后进先出)的原则存储数据。(引用)

            但是我们为了简单理解函数栈帧,我们不需要很深入的了解,就只需要明白栈帧在内存中的存在形式...

             

       理解这个图.

       下面是高地址,上面是高地址,可以理解函数栈帧中,汇编命令从高地址向低地址访问改变和操控.

        这个栈,或者说这些值和其地址以这种形式存在于内存中,你访问就是内存.

推荐可以看一眼

汇编语言(不是很有必要学习,提供一个大佬的文章)

汇编语言入门教程 - 阮一峰的网络日志 (ruanyifeng.com)icon-default.png?t=N7T8https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html


正式内容 

我们以vs2022为例子

 使用代码部分

int add(int x, int y)
{
    int n = 0;
    n = x + y;
    return n;
}

int main()
{
    int a = 1;
    int b = 2;
    int c = 5;
    c = add(a, b);
    printf("%d", c);
    return 0;
}

然后是打开反汇编

寄存器部分

首先是

00007FF79D985BB0  push        rbp  
00007FF79D985BB2  push        rdi  

这两个命令是 rbp 和 rdi 入栈

 00007FF79D985BB3  sub         rsp,148h  

申请148h字节内存

 00007FF79D985BBA  lea         rbp,[rsp+20h] 

00007FF79D985BBF  lea         rcx,[__0C5A5099_121@c (07FF79D992008h)] 

把rsp+20h的值塞入lea中,也就是为add申请20h字节的内存

 

    int a = 1;
00007FF79D985BCB  mov         dword ptr [rbp+4],1  
    int b = 2;
00007FF79D985BD2  mov         dword ptr [rbp+24h],2  
    int c = 5;
00007FF79D985BD9  mov         dword ptr [rbp+44h],5  

 分别为每个内存申请一个空间和带入数字

此时

00007FF79D985BE0  mov         edx,dword ptr [rbp+24h]  
00007FF79D985BE3  mov         ecx,dword ptr [rbp+4]  
00007FF79D985BE6  call        00007FF79D9813E3  
00007FF79D985BEB  mov         dword ptr [rbp+44h],eax 

 rbp+4 是a的地址 rbp+24h是b的地址 rbp+44h是c的地址 他们并不是连续存在的

上面的寄存器edx ecx 分别记录了a的值 c的值 并且记录在上面

现在是这个情况

然后是

 

 00007FF79D981DE0  mov         qword ptr [rsp+8],rcx  
00007FF79D981DE5  sub         rsp,38h  

 录入rcx 向上推rsp 现在理解 edx就是x的值  ecx就是y的值 这里的供add使用

这里是把rcx的值放到rsp上面+8地址内 rsp再向上移动38h个字节

 

 00007FF79D981DE9  mov         rax,qword ptr [rsp+40h]  
00007FF79D981DEE  mov         qword ptr [rsp+20h],rax

把rax移动到rsp+40h 把 rsp+20h移动到rax 

00007FF79D981DE0  mov         qword ptr [rsp+8],rcx  
00007FF79D981DE5  sub         rsp,38h  
    unsigned char *__DebuggerLocalJMCFlag = JMC_flag;
00007FF79D981DE9  mov         rax,qword ptr [rsp+40h]  
00007FF79D981DEE  mov         qword ptr [rsp+20h],rax  

    if (*JMC_flag && __DebuggerCurrentSteppingThreadId != 0 && __DebuggerCurrentSteppingThreadId == GetCurrentThreadId()) {
00007FF79D981DF3  mov         rax,qword ptr [rsp+40h]  
00007FF79D981DF8  movzx       eax,byte ptr [rax]  
00007FF79D981DFB  test        eax,eax  
00007FF79D981DFD  je          00007FF79D981E17  
00007FF79D981DFF  cmp         dword ptr [00007FF79D98D94Ch],0  
00007FF79D981E06  je          00007FF79D981E17  
00007FF79D981E08  call        qword ptr [00007FF79D991088h]  
00007FF79D981E0E  cmp         dword ptr [00007FF79D98D94Ch],eax  
00007FF79D981E14  jne         00007FF79D981E17  
NopLabel:
        __nop();
00007FF79D981E16  nop  
    }
}
00007FF79D981E17  add         rsp,38h 

经过上列过程开辟新函数的内容

00007FF79D985BE0  mov         edx,dword ptr [rbp+24h]  
00007FF79D985BE3  mov         ecx,dword ptr [rbp+4

00007FF79D985BE6  call        00007FF79D9813E3  
00007FF79D985BEB  mov         dword ptr [rbp+44h],eax 

其中

00007FF79D985BE6  call        00007FF79D9813E3  

 利用call调用add函数,把rbp+24h(edx) 和 rbp+4(ecx)的值在add中调用的返回值放入到eax中

然后把eax的值放入 rbp+44h(也就是c的地址) 完成了c的赋值


00007FF79D985BFD  xor         eax,eax  
}
00007FF79D985BFF  lea         rsp,[rbp+0000000000000128h]  
00007FF79D985C06  pop         rdi  
00007FF79D985C07  pop         rbp 

00007FF79D985C08  ret

这里是return 0;后面的

大致计算销毁占用的内存 然后返回系统

 然后结束

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

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

相关文章

C++ OpenGL 3D Game Tutorial 2: Making OpenGL 3D Engine学习笔记

视频地址https://www.youtube.com/watch?vPH5kH8h82L8&listPLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg&index3 一、main类 接上一篇内容&#xff0c;main.cpp的内容增加了一些代码&#xff0c;显得严谨一些&#xff1a; #include<OGL3D/Game/OGame.h> #include<i…

寒假前端第一次作业

1、用户注册&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>用户注册</title> …

C++学习笔记——string类和new函数

目录 string类 1.功能增强 1.1 子字符串提取 1.2 字符串拼接 1.3 大小写转换 1.4 字符串比较 2.性能优化 3.使用示例 下面是一个简单的使用示例&#xff0c;展示了如何使用改进后的String类&#xff1a; NEW函数 2.1NEW函数的基本用法 2.2NEW函数的注意事项 2.3避…

使用lwip的perf进行测速TCP不稳定的一些相关配置项

在使用lwIP的perf工具进行TCP性能测试时&#xff0c;TCP不稳定可能涉及以下配置问题&#xff1a; 缓冲区大小&#xff08;Buffer Size&#xff09;&#xff1a;lwIP中的TCP性能受到发送和接收缓冲区大小的影响。如果缓冲区过小&#xff0c;可能导致数据包丢失或延迟增加&#x…

《BackTrader量化交易图解》第8章:plot 绘制金融图

文章目录 8. plot 绘制金融图8.1 金融分析曲线8.2 多曲线金融指标8.3 Observers 观测子模块8.4 plot 绘图函数的常用参数8.5 买卖点符号和色彩风格8.6 vol 成交参数8.7 多图拼接模式8.8 绘制 HA 平均 K 线图 8. plot 绘制金融图 8.1 金融分析曲线 BackTrader内置的plot绘图函…

Hibernate实战之操作MySQL数据库(2024-1-8)

Hibernate实战之操作MySQL数据库 2024.1.8 前提环境&#xff08;JavaMySQLNavicatVS Code&#xff09;1、Hibernate简介1.1 了解HQL 2、MySQL数据库建表2.1 编写SQL脚本2.2 MySQL执行脚本 3、Java操作MySQL实例&#xff08;Hibernate&#xff09;3.1 准备依赖的第三方jar包3.2 …

密码学:一文读懂非对称加密算法 DH、RSA

文章目录 前言非对称加密算法的由来非对称加密算法的家谱1.基于因子分解难题2.基于离散对数难题 密钥交换算法-DH密钥交换算法-DH的通信模型初始化DH算法密钥对甲方构建DH算法本地密钥乙方构建DH算法本地密钥DH算法加密消息传递 典型非对称加密算法-RSARSA的通信模型RSA特有的的…

大数据StarRocks(六) :Catalog

StarRocks 自 2.3 版本起支持 Catalog&#xff08;数据目录&#xff09;功能&#xff0c;实现在一套系统内同时维护内、外部数据&#xff0c;方便您轻松访问并查询存储在各类外部源的数据。 1. 基本概念 内部数据&#xff1a;指保存在 StarRocks 中的数据。 外部数据&#xf…

用css给宽高不固定的矩形画对角线

.kong{width: 200rpx;height: 76rpx;background: linear-gradient(to bottom right, #E5E5E5 0%, rgba(0, 0, 0, 0.1) calc(50% - 1px),#175CFF 50%, rgba(0, 0, 0, 0.1) calc(50% 1px),rgba(0, 0, 0, 0.1) 100%);}参考&#xff1a; https://blog.csdn.net/weixin_38779534/a…

1.1map

unordered_map和map的使用几乎是一致的&#xff0c;只是头文件和定义不同 #include<iostream> #include<map>//使用map需要的头文件 #include<unordered_map>//使用unordered_map需要的头文件 #include<set>//使用set需要的头文件 #include<uno…

web前端(html)练习

第一题 1. 用户名为文本框&#xff0c;名称为 UserName&#xff0c;长度为 15&#xff0c;最大字符数为 20。 2. 密码为密码框&#xff0c;名称为 UserPass&#xff0c;长度为 15&#xff0c;最大字符数为 20。 3. 性别为两个单选按钮&#xff0c;名称为 sex&#xff0c;值分…

【linux】tcpdump 使用

tcpdump 是一个强大的网络分析工具&#xff0c;可以在 UNIX 和类 UNIX 系统上使用&#xff0c;用于捕获和分析网络流量。它允许用户截取和显示发送或接收过网络的 TCP/IP 和其他数据包。 一、安装 tcpdump 通常是默认安装在大多数 Linux 发行版中的。如果未安装&#xff0c;可…

使用lwip的perf进行测速TCP会有较多的duplicate ack的原因分析

在使用lwIP的perf工具进行TCP测速时&#xff0c;出现较多的重复确认&#xff08;duplicate ACK&#xff09;可能是由于以下原因导致的&#xff1a; 丢包或乱序&#xff1a;重复确认通常是由于网络中的数据包丢失或乱序到达引起的。当接收方收到一个乱序的数据包时&#xff0c;它…

imazing破解版百度云2.17.3(附激活许可证下载)

iMazing是一款强大的 iOS 设备管理软件&#xff0c;不管是 iPhone、iPad 或 iPod Touch 设备&#xff0c;只要将 iOS 设备连接到计算机&#xff0c;就可以处理不同类型的数据。 iPhone 和 iPad 备份 借助 iMazing 的独有 iOS 备份技术&#xff08;无线、隐私和自动&#xff09…

系列十三、查询数据库中某个库、表、索引等所占空间的大小

一、information_schema数据库 1.1、概述 information_schema数据库是MySQL出厂默认带的一个数据库&#xff0c;不管我们是在Linux中安装MySQL还是在Windows中安装MySQL&#xff0c;安装好后都会有一个数据库information_schema&#xff0c;这个库中存放了其他库的所有信息。 …

【UE Niagara学习笔记】02 - 制作燃烧的火焰

目录 效果 步骤 一、添加资产 二、制作材质 三、制作粒子 3.1 循环播放 3.2 粒子生成的数量 3.3 粒子的生命周期和初始大小 3.4 火焰高度 3.5 火焰范围 3.6 火焰颜色 效果 步骤 一、添加资产 1. 在虚幻商城中搜索“M5 VFX Vol2. Fire and Flames(Niagara)”…

网络协议与攻击模拟_01winshark工具简介

一、TCP/IP协议簇 网络接口层&#xff08;没有特定的协议&#xff09; 物理层&#xff1a;PPPOE宽带拨号&#xff08;应用场景&#xff1a;宽带拨号&#xff0c;运营商切网过来没有固定IP就需要拨号&#xff0c;家庭带宽一般都采用的是拨号方式&#xff09;数据链路层网络层…

kettle的基本介绍和使用

1、 kettle概述 1.1 什么是kettle Kettle是一款开源的ETL工具&#xff0c;纯java编写&#xff0c;可以在Window、Linux、Unix上运行&#xff0c;绿色无需安装&#xff0c;数据抽取高效稳定。 1.2 Kettle核心知识点 1.2.1 Kettle工程存储方式 以XML形式存储以资源库方式存储…

【React源码 - Fiber架构之Reconciler】

前言 React16架构可以分为三层也是最核心的三个功能分别是&#xff1a; Scheduler&#xff08;调度器&#xff09;—调度任务的优先级&#xff0c;高优任务优先进入Reconciler(16新增)Reconciler&#xff08;协调器&#xff09;—负责找出变化的组件Renderer&#xff08;渲染…

Android readelf 工具查找函数符号

ELF&#xff08;Executable and Linkable Format&#xff09;是一种执行文件和可链接文件的格式。它是一种通用的二进制文件格式&#xff0c;用于在各种操作系统中存储可执行程序、共享库和内核模块。 Android 开发当中的 so 库本质上就是一种特殊类型的 ELF 文件&#xff0c;…