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

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

博客引用相关文献:
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


目录
相关文章
|
2月前
|
存储 安全 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
|
2月前
|
存储 编译器 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
|
程序员 编译器 C语言
细谈函数栈帧的创建与销毁
我们在写C语言代码时,经常会把一个独立的功能抽象为函数,所以C程序是以函数为基本单位的。那函数如何调用?函数的返回值如何返回的?函数参数是如何传递的?这些问题都与函数栈帧有关系。
141 0
|
9月前
|
存储 机器学习/深度学习 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(一)
179 0
|
9月前
|
存储 编译器
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
函数栈帧深度剖析(一篇带你牢牢掌握函数栈帧)(二)
68 0
|
11月前
|
存储 编译器 程序员
C语言代码函数栈帧的创建与销毁(修炼内功)
目录 在前期的学习中我们可能有很多困惑 例如:局部变量是怎么创建的 为什么局部变量的值是随机值 函数是怎么样传参的 传参的顺序是什么 形参和实参的关系是什么 函数调用是怎么做的 函数掉调用结束后怎么返回的 这篇博客我们来修炼自己的内功,掌握好这篇博客的大部分知识就已经很不错了 我们用到VS2013这个编译器,目的是为了看到更详细的函数封装内容 提示不要使用太过高级的编译器,因为越高级的编译器越不容易观察。同时这里需要注意的是在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,不是完全相同的,具体细节取决于编译器
|
11月前
|
C语言
抽丝剥茧C语言(中阶)函数栈帧的创建与销毁——图解(下)
抽丝剥茧C语言(中阶)函数栈帧的创建与销毁——图解
|
11月前
|
编译器 C语言
抽丝剥茧C语言(中阶)函数栈帧的创建与销毁——图解(上)
抽丝剥茧C语言(中阶)函数栈帧的创建与销毁——图解
|
11月前
|
存储 编译器 C语言
函数栈帧的创建与销毁(反汇编万字讲解)
局部变量是怎么创建的? 为什么未初始化的局部变量的值是随机值? 函数是怎样传参的?传参的顺序是怎样的? 形参和实参是什么关系? 函数调用是怎样做的? 函数调用后是怎样返回的? 我们本章就来研讨这个问题,掌握了函数栈帧的创建和销毁更有利于后期的学习这里建议大家要从头往后一个内容一个内容看,因为这里每一个部分关联性很强!
|
11月前
|
编译器
【函数栈帧的创建和销毁】 -- 神仙级别底层原理,你学会了吗?(1)
1.函数的调用方式 相信你对调用函数一点都不陌生,但是在调用函数的过程中,却存在着很多你无法见到的东西,这是底层信息,想要理解透彻,就得深入底层去观察。 本文以一个最简单的加法函数为例,深入讲解内存空间中的每一条指令。