探秘函数栈帧:『 揭开函数栈帧创建与销毁的神秘面纱 』(一)

简介: 探秘函数栈帧:『 揭开函数栈帧创建与销毁的神秘面纱 』

博客引用相关文献:
1.《程序员的自我修养——链接、装载与库》


2.西安比特教育科技.《C语言进阶_动态内存管理》


👻内容专栏:《C/C++学习专栏》


🐨本文概括:讲解函数栈帧创建与销毁的具体过程


🐼本文作者:花 碟


🐸发布时间:2023.4.19


知识点回顾

前期我们学习的时候,对许多知识可能有很多困惑。


比如:


局部变量是怎么创建的?

为什么局部变量创建后默认是随机值?

函数是怎么传参的?传参的顺序是怎么样的?

形参和实参的关系是怎么样的?

函数调用是怎么做的?

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

还弄不清?没关系,相信聪明的小伙伴们学习此篇章的函数栈帧知识,以上问题就会迎刃而解啦!


OK,让我们来揭开函数栈帧的创建与销毁的神秘面纱吧~~


一、什么是栈帧(堆栈帧)?

在了解函数栈帧之前,我们不得不先了解一下内存布局、寄存器、汇编指令相关概念。


1.内存布局

C/C++内存布局中的几个区域:

1.1.png

1.栈区(stack): 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。栈是向下增长的。栈区是向下生长的,也就是说,栈顶的地址是最小的。栈区的大小是有限制的,如果超出了栈的大小,就会发生栈溢出错误。


2.堆区(heap):堆区是由程序员手动分配和释放的内存区域,用于存储动态分配的内存。堆区是向上生长的,也就是说,堆顶的地址是最大的。堆区的大小是没有限制的,但是如果没有及时释放内存,就会导致内存泄漏。


3.数据段(静态区static):存放全局变量、静态数据。程序结束后由系统释放。


4.代码段:存放函数体(类成员函数和全局函数)的二进制代码。


2.常用寄存器

eax、ebx、ecx、edx、esp、ebp是x86架构CPU中比较常见的寄存器,它们的含义和作用如下:


eax寄存器:又称为累加器寄存器(Accumulator Register),通常用于存储算术计算的操作数和结果。在函数调用时,EAX寄存器也用于保存函数返回值。


ebx寄存器:又称为基址寄存器(Base Register),通常用于存储内存地址信息。在处理数据时,EBX寄存器可以作为一个指针来访问内存。


ecx寄存器:又称为计数器寄存器(Counter Register),通常在循环中使用,用于对循环次数进行计数,并控制循环的结束。


edx寄存器:又称为数据寄存器(Data Register),通常用来存储数据,例如两个操作数的乘积,以及一些特定系统调用的参数。


esp寄存器:又称为栈指针寄存器(Stack Pointer Register),用于指向当前栈顶的位置,也就是最后压入栈中的数据的地址。在函数调用时,ESP寄存器也用于保存当前函数的栈帧信息。


ebp寄存器:又称为基址指针寄存器(Base Pointer Register),用于指向当前栈帧的基地址,也就是当前函数栈帧在栈中的起始位置。在函数调用时,EBP寄存器可以用来定位本地变量和函数参数。


esi寄存器:又称为源索引寄存器(Source Index Register),通常用于存放源数据地址,在计算机复制、移动、传输等操作中扮演着重要角色。


edi寄存器:又称为目标索引寄存器(Destination Index Register),通常用于存放目标数据地址,在计算机复制、移动、传输等操作中也扮演着重要角色


3.汇编指令

MOV:将数据从一个地方移动到另一个地方,例如将一个寄存器中的值移动到另一个寄存器、内存地址或立即数中。


ADD/SUB:加法和减法指令,可以将两个操作数相加或相减,并将结果保存在目标寄存器或内存位置中。


CMP:比较两个操作数并设置标志位,用于支持条件跳转等操作。


JMP:无条件跳转指令,跳转到指定的代码位置执行。


CALL/RET:用于函数的调用与返回,CALL指令将当前程序计数器(PC)入栈并跳转到指定位置,RET指令从堆栈中弹出PC并跳转回调用函数的位置。


PUSH/POP:用于堆栈操作,PUSH指令将数据压入堆栈顶,POP指令将数据从堆栈顶弹出。


NOP:空指令,不执行任何操作,通常用于占位或调试。


XOR/OR/AND:逻辑运算指令,XOR进行异或运算,OR进行或运算,AND进行与运算。


👇👇对于栈的详细介绍 :

🏷️在经典的计算机科学中,栈被定义为一个特殊的容器,用户可以将数据压入栈中(简称:压栈,push),也可以将已经压入栈中的数据弹出(简称:出栈,pop),但栈这个容器必须遵守一条规则:先入栈的数据后出栈(First In Last Out,FIFO),多多少少像叠成一摞的书籍📚:先叠上去的书在最下面,因此要最后才能取出。

1.2.png

🏷️ 栈是一个具有以上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使得栈增大,而出栈操作使栈减小。


👇👇函数栈帧的介绍:

esp(Extended Stack Pointer)为扩展栈指针寄存器,是指针寄存器的一种,用于存放函数栈顶指针。与之对应的是ebp(Extended Base Pointer),扩展基址指针寄存器,也被称为帧指针寄存器,用于存放函数栈底指针。


我们知道,在操作系统里,栈总是向下增长的(高地址向低地址增长)。在i386下,栈顶由esp的寄存器进行定位。压栈的操作使栈顶的地址减小,弹出的操作使栈顶地址增大。观察下方描述图,这里栈底指针的地址是0xbfffffff,而esp栈顶指针标明了栈顶,地址为0xbffffff4。在栈上压入数据会导致esp减小,弹出数据使得esp增大。相反,直接减小esp的值也等效于在栈上开辟空间,直接增大esp的值等价于在栈上回收空间。栈保存了一个函数调用所需要的维护信息,通常由esp以及ebp两个寄存器来维护,常被称为栈帧(Stack Frame)  

1.3.png


目录
相关文章
|
7月前
|
存储 编译器 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
|
7月前
|
存储 安全 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
|
程序员 编译器 C语言
细谈函数栈帧的创建与销毁
我们在写C语言代码时,经常会把一个独立的功能抽象为函数,所以C程序是以函数为基本单位的。那函数如何调用?函数的返回值如何返回的?函数参数是如何传递的?这些问题都与函数栈帧有关系。
186 0
|
存储 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
121 0
|
存储 机器学习/深度学习 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
761 0
|
存储 编译器 程序员
|
编译器 C语言
函数的栈帧与销毁(栈帧可不是战争哦)
函数的栈帧与销毁(栈帧可不是战争哦)
57 0
|
存储 编译器 程序员
C语言代码函数栈帧的创建与销毁(修炼内功)
目录 在前期的学习中我们可能有很多困惑 例如:局部变量是怎么创建的 为什么局部变量的值是随机值 函数是怎么样传参的 传参的顺序是什么 形参和实参的关系是什么 函数调用是怎么做的 函数掉调用结束后怎么返回的 这篇博客我们来修炼自己的内功,掌握好这篇博客的大部分知识就已经很不错了 我们用到VS2013这个编译器,目的是为了看到更详细的函数封装内容 提示不要使用太过高级的编译器,因为越高级的编译器越不容易观察。同时这里需要注意的是在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,不是完全相同的,具体细节取决于编译器
|
存储 监控 编译器
【C语言进阶】函数栈帧的创建和销毁(内功修炼)
目录 前言 一、基础知识 1.1 什么是栈区? 1.2 寄存器 1.3 测试代码和一些其它的 二、函数栈帧的创建和销毁的过程 2.1 _tmainCRTStartup函数(调用main函数)栈帧的创建 2.2 main函数栈帧的创建 2.3 main函数内执行有效代码 2.4 Add函数栈帧的创建 2.5 Add函数内执行有效代码 2.6 Add函数栈帧的销毁 2.7 main函数代码继续执行 三、所需反汇编代码总览 四、总结
325 0
【C语言进阶】函数栈帧的创建和销毁(内功修炼)
|
存储 编译器
从汇编代码探究函数栈帧的创建和销毁的底层原理(二)
从汇编代码探究函数栈帧的创建和销毁的底层原理