一. 理解栈帧

栈帧是什么,我们基本的理解是栈帧也叫活动记录过程,是编译器用来实现过程
函数调用的一种数据结构。通俗来说栈帧就时C语言函数在调用的过程中的调用原理,就是当我们执行一个函数操作的时候,它的内部是如何实现的呢。

二 .关于栈帧的背景知识

  1. 寄存器

第一个寄存器ebp,基址寄存器,也叫做栈底寄存器。

第二个寄存器esp,是栈顶寄存器。

第三个寄存器pc指针,也叫做程序计数器,它永远指向当前指令的下一条指令。

  1. 计算机运算的基本过程

取指令–分析指令–执行指令

但程序执行的过程中,pc指针指向下一个指令,那么当一个函数执行的时候,它会指向这个 函数,另外任何一个函数都有自己的ebp和esp,即是任何一个函数都有一个栈底和栈顶。但是在我们的cpu中,ebp和esp不可能有很多,可是我们的函数却可以很多,所以在函数执行的时候,ebp和esp都被新的函数覆盖掉了,那个原来的ebp和esp都应该保存,这样之后我们在执行完一个函数之后才可以回到上面一个函数。所以我们的ebp和esp始终指向当前函数的栈底和栈顶。

  1. 栈地址的生长方向

栈的生长方向是从高地址往低地址生长的,那么随着我们函数的调用的层层深入,我们的ebp和esp会越来越小。

  1. 程序地址空间

这里先画一个草图,让大家看一下一个程序在运行的时候需要哪些地址空间。

今天我们主要讨论的是栈区里面的使用原理,下面就是栈帧调用的详细解析

三. 栈帧调用原理

这里讨论函数调用的原理,我们主要画一个栈和代码区的一个图。下面的这幅图简明的说明了栈帧调用的机理。

在函数的最开始执行的应该是调用main函数,那么此时应该为main函数开辟一个存储空间,并且有一个ebp和一个esp指向这个存储空间的栈顶和栈底,另外还有pc指针指向代码区中的main函数中的下一条指令。这里再次说明一下,
栈里面是由高地址往低地址生长,请看下图,其中红色箭头代表main函数的指针,蓝色箭头代表指向fun函数的指针,这里我们假设首先执行main函数,然后执行fun函数,首先使用红色指针,然后使用蓝色指针。

这里说明了一个问题就是,当程序执行不同的函数时,我们的ebp、esp和pc指针是变化的,并且执向当前函数的栈帧区,因为我们执行完当前函数之后还希望返回原来的函数,那么我们在改变这三个指针之前一定要先保存这三个指针。上图只是说明我们要保存三个指针变量但是没有说明具体是如何保存的,还有就是函数中是要开辟参数的,我们的草图也没有说明参数的空间时如何开辟的。

接下来我们通过具体的代码来详细的说明一下,在编译器的内部是如何进行栈帧的变化的。首先看下面的一段代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
int fun(int a, int b)
{int c = 0xcccccccc;
return c;
}
int main()
{int a = 0xaaaaaaaa; //这里赋值为0xaaaaaaaa的原因是,在一会的查看汇编时候,
//显示的都是16进制,这里这样赋值为了我们容易观察
int b = 0xbbbbbbbb;
int ret = fun(a, b);
printf("You shoud running here!\n");
system("pause");
return 0;
}

对于这段代码,我们可以按F10进入调试,然后选择菜单栏的调试,选择反汇编,然后一次次按F10知道定义a变量的时候,看下面的截图。

这里看红色框里面,实际上在汇编语言中,定义并初始化a,b变量的时候实际上是把一个值放入到一个内存当中去,中间用到了mov指令。那么我们此时也应该在主函数的栈帧区为a,b变量分别开辟空间,请看下图。

接下来我们基础回到我们的汇编代码,请看我给大家的截图

这里补充说明的一点,汇编代码中的call实际上就是调用函数的过程,但是我们还需要明白的一点就是,当我们调用这个函数的时候,数据应该是已经创建好的,所以我们这里在call指令调用这个函数的前面是一个传递参数的工作。这里解释一点就是eax,ebx…都是通用寄存器。第一个红框里面mov指令,把b变量放置在eax中,然后执行push指令,该指令的作用是把b变量放置在栈顶,第二个红色框中,把a变量放置在ecx中,然后push变量a入栈。push完成过后就形成了形参实例化的a和b的临时变量。从上面的实例化的过程中,我们不难发现一个问题就是,参数在实例化的时候的顺序是从右往左的。

现在我们再来看看栈帧图是如何压栈的。

这里的压栈,即汇编代码中的push,是首先把指针往下移动,然后把数据形参b放进栈顶,然后再使指针往下移动,然后再放入数据形参a到栈顶位置。

把a,b的形参压如栈顶之后,才真正开始执行call指令,call指令的作用是,第一个作用是把当前指令的下一条指令的地址压入栈中,当前指令使call指令,它的下一条指令是add,从上面的反汇编图中可以看到,这里我们还可以看到add的指令地址为01011449,那么这个地址就被压入栈中;call的第二个作用是跳转(jmp)到下一个函数的入口。我们按F11就可以进入到jmp这个函数中去,jmp的一个作用是修改pc指针。接下来按F10就可以执行我们的fun函数。

这个时候我们进入到了它的fun函数内部,继续查看它的反汇编代码,请看下面的反汇编的截图

第一句,push ebp即是把main函数的栈底指针压栈。接下来是mov ebp,esp它的作用就是把ebp指向当前函数的栈底。再下一条指令,sub esp,0CCh,这条指令作用是使esp往下移一段空间,这样做之后,我们的ebp和esp又指向了一个新的空间,这个空间就是我们的fun函数的栈帧空间。

再来看上图中的最下面的两个红色框中,这个时候fun函数已经执行完毕,mov esp,ebp,就是把ebp中的内容放在esp中去,那么此时的结果就是esp又指向了存放main函数的ebp的地址空间,请看黑色箭头。接下来又执行了pop ebp,就是恢复ebp的值,那么此时ebp又指向了main函数的最开始的ebp中去了,然后esp往上退回一个地址空间,那么此时esp就是main:ret,也就是刚刚的那个call指令的下一条指令add指令。接下来反汇编代码中最后一个指令ret,这个指令被集成了,我们无法看到,它的作用是继续pop,它的其中一个操作是pop pc,那么就是把当前esp中的内容放在了pc指针中,此时pc指针指向的就是add指令,接着再让esp退回一段地址空间,所以ret执行完毕之后,esp又指回了临时变量a。请看下面的栈帧图

接下来我们按F10这样又回到了主函数当中去了,那个此时执行了add指令,请看下面的反汇编截图

紧接着它做了下一步操作add esp,8,就是把esp中的内容加上8,加上8之后,esp刚好指向了main函数原来的esp,至此esp和ebp都回到了原来的main的栈帧结构中了。

请看最后一张图,简化后的图

栈帧详解ebp、esp相关推荐

  1. 帧栈使用的基本用法c语言,栈帧详解

    一. 理解栈帧 栈帧是什么,我们基本的理解是栈帧是栈帧也叫过程 活动记录,是 编译器用来实现过程/ 函数调用的一种数据结构.通俗来说栈帧就时C语言函数在调用的过程中的调用原理,就是当我们执行一个函数操 ...

  2. 函数调用过程以及栈帧详解

    函数的调用是一个过程,那么在函数的调用过程中要开辟栈空间,用来对本次函数的调用中需要的临时变量保存.这块空间叫栈帧.这个过程调用包括将数据和控制从代码的一部分传递到另一部分.过程调用的任务:为过程的局 ...

  3. java语言链栈_Java语言实现数据结构栈代码详解

    近来复习数据结构,自己动手实现了栈.栈是一种限制插入和删除只能在一个位置上的表.最基本的操作是进栈和出栈,因此,又被叫作"先进后出"表. 首先了解下栈的概念: 栈是限定仅在表头进行 ...

  4. Unwind 栈回溯详解:libunwind

    目录 1. 历史背景 1.1 frame pointers 1.2 .debug_frame (DWARF) 1.3 .eh_frame (LSB) 1.4 CFI directives 2. .de ...

  5. Unwind 栈回溯详解

    文章目录 1. 历史背景 1.1 frame pointers 1.2 .debug_frame (DWARF) 1.3 .eh_frame (LSB) 1.4 CFI directives 2. . ...

  6. c语言 栈结构存放数据类型,数据结构——栈的详解

    栈和队列是两种重要的线性结构,从数据结构的角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表的子集.他们是操作受限的线性表,因此,可称为限定性的数据结构.但从数据类型角度看,他们是和线 ...

  7. 【Wi-Fi 802.11协议】管理帧 之 Beacon帧详解

    Beacon帧简介 信标帧,由AP以一定的时间间隔周期性发出,以此来告诉外界自己无线网络的存在. Beacon帧组成 下图为Beacon帧的组成 下图为抓包所得(AP为2.4g 11n模式),Pack ...

  8. python数据科学常国珍_《PYTHON数据科学:全栈技术详解》常国珍//赵仁乾//张秋剑著【摘要 书评 在线阅读】-苏宁易购图书...

    商品参数 作者: 常国珍//赵仁乾//张秋剑著 出版社:机械工业出版社 出版时间:2018-07-01 00:00:00 版次:1 印次:1 印刷时间:2018-07-01 字数:250 页数:422 ...

  9. PowerPC构架应用程序二进制接口(ABI)及堆栈帧详解

    第一部分 概述 应用程序二进制接口(ABI-Application Binary Interface)定义了一组在PowerPC系统软件上编译应用程序所需要遵循的一套规则.主要包括基本数据类型,通用寄 ...

最新文章

  1. LogMiner日志分析工具的使用
  2. 代码格式化工具 uncrustify 配置文件选项详解
  3. mapreduce shuffle过程
  4. 思科网络技术学院CCNA教程
  5. 为什么要加上拉电阻和下拉电阻
  6. 2020-06-18 CVPR2020 VL论文讨论(1) 笔记
  7. 最长递增子序列的个数Python解法
  8. 宣布JavaScript 2017状况调查
  9. java jsp 传递参数的方法,jsp传参方法小结
  10. HTML5导航栏菜单的设计与实现
  11. c语言求闰年平年的计算方法,C语言平年闰年问题
  12. windows 实验报告
  13. AdBlock插件自定义拦截规则
  14. 上海车牌拍牌辅助工具
  15. 意创坊-移动富媒体平台
  16. access函数的用法
  17. 微信公众号申请所需材料汇总
  18. spark写出分布式的训练算法_Spark on Angel
  19. ECMAScript标准简介
  20. 图形 1.1渲染流水线(知识梳理笔记)

热门文章

  1. IT人才外包的合作流程是怎样的?
  2. 数据集成解决方案探讨
  3. JAVA权重算法(如Dubbo的负载均衡权重)
  4. Javascript-API-BOM、动画函数、网页轮播图、节流阀、筋斗云、固定侧边栏返回顶部案例
  5. 【Nunit入门系列讲座 1】Nunit的安装及功能介绍
  6. Centos7.5 BCM4322无线网卡驱动安装踩坑记录
  7. 天数换算月份_EXCEL如何算出指定的月份有多少天计算方法
  8. 台式计算机 主控芯片型号,win10系统查看U盘的主控芯片型号的图文方法
  9. 最快速的TeamTalk 服务器部署方
  10. 前端面试题 ~ 移动端注意