栈中函数调用原理_详解

2023-05-16



 

           
函数调用是程序设计中的重要环节,本文就函数调用的过程进行分析。

一、eip、ebp、esp介绍

 EIP,EBP,ESP都是系统的寄存器,里面存储的是些地址,我们系统中栈的实现上离不开他们三个。 我知道栈的数据结构主要特点是 后进先处。它还有两个作用: 1.栈是用来存储临时变量,函数传递的中间结果。 2.操作系统维护的,对于程序员是透明的。

下面我们就通过一个小例子说说栈的原理。

先写个小程序:


 1
 2
 3
 4
 5
 6
 7
 8
 9  

            
            
void fun(void)
{
printf("helloworld");
}
void main(void)
{
fun()
printf("函数调用结束");
}
来自CODE的代码片
snippet_file_0.txt

当程序进行函数调用的时候,我们经常说的是先将函数压栈,当函数调用结束后,再出栈。这一切的工作都是系统帮我们自动完成的。但在完成的过程中,系统会用到下面三种寄存器:EIP、ESP、EBP。

当调用fun函数开始时,三者的作用。

  1. EIP寄存器里存储的是CPU下次要执行的指令的地址。 也就是调用完fun函数后,让CPU知道应该执行main函数中的printf("函数调用结束")语句了。
  2. EBP寄存器里存储的是是栈的栈底指针,通常叫栈基址,这个是一开始进行fun()函数调用之前,由ESP传递给EBP的。(在函数调用前你可以这么理解:ESP存储的是栈顶地址,也是栈底地址。)
  3. ESP寄存器里存储的是在调用函数fun()之后,栈的栈顶。并且始终指向栈顶。
当调用fun函数结束后,三者的作用:

  1. 系统根据EIP寄存器里存储的地址,CPU就能够知道函数调用完,下一步应该做什么,也就是应该执行main函数中的printf(“函数调用结束”)。
  2. EBP寄存器存储的是栈底地址,而这个地址是由ESP在函数调用前传递给EBP的。等到调用结束,EBP会把其地址再次传回给ESP。所以ESP又一次指向了函数调用结束后,栈顶的地址。

二、堆和栈

首先要清楚的是程序对内存的使用分为以下几个区:

  1. 栈区(stack):由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似于数据结构中的栈。
  2. 堆区(heap):一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表。
  3. 全局区(static):全局变量和静态变量存放在此。
  4. 文字常量区:常量字符串放在此,程序结束后由系统释放。
  5. 程序代码区:存放函数体的二进制代码。

典型的内存区域分配如图所示:


其次是堆和栈的申请方式:栈由系统自动分配,速度较快,在windows下栈是向低地址扩展的数据结构,是一块连续的内存区域,大小是2MB。堆需要程序员自己申请,并指明大小,速度比较慢。在C中用malloc,C++中用new。另外,堆是向高地址扩展的数据结构,是不连续的内存区域,堆的大小受限于计算机的虚拟内存。因此堆空间获取和使用比较灵活,可用空间较大。 

三、栈帧结构和函数调用过程

首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。下图为典型的存取器安排,观察栈在其中的位置


入栈操作:push eax; 等价于 esp=esp-4,eax->[esp];如下图


出栈操作:pop eax; 等价于 [esp]->eax,esp=esp+4;如下图


我们来看下面这个C程序在执行过程中,栈的变化情况


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11  

            
            
void func(int m, int n) {
int a, b;
a = m;
b = n;
}
main() {
...
func(m, n);
L: 下一条语句
...
}
来自CODE的代码片
snippet_file_0.txt

在main调用func函数前,栈的情况,也就是说main的栈帧:


从低地址esp到高地址ebp的这块区域,就是当前main函数的栈帧。当main中调用func时,写成汇编大致是:

push m

push n; 两个参数压入栈

call func; 调用func,将返回地址填入栈,并跳转到func


当跳转到了func,来看看func的汇编大致的样子:

__func:

push ebp; 这个很重要,因为现在到了一个新的函数,也就是说要有自己的栈帧了,那么,必须把上面的函数main的栈帧底部保存起来,栈顶是不用保存的,因为上一个栈帧的顶部讲会是func的栈帧底部。(两栈帧相邻的)

       mov ebp, esp; 上一栈帧的顶部,就是这个栈帧的底部;暂时先看现在的栈的情况


到这里,新的栈帧开始了

sub esp, 8;  int a, b 这里声明了两个int,所以esp减小8个字节来为a,b分配空间

mov dword ptr [esp+4],[ebp+12];   a=m

mov dword ptr [esp], [ebp+8];b=n         

这样,栈的情况变为:


ret 8 ;  返回,然后8是什么意思呢,就是参数占用的字节数,当返回后,esp-8,释放参数m,n的空间。由此可见,通过ebp,能够很容易定位到上面的参数。当从func函数返回时,首先esp移动到栈帧底部(即释放局部变量),然后把上一个函数的栈帧底部指针弹出到ebp,再弹出返回地址到cs:ip上,esp继续移动划过参数,这样,ebp,esp就回到了调用函数前的状态,即现在恢复了原来的main的栈帧。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

栈中函数调用原理_详解 的相关文章

  • 配置文件的方式总结

    1 xml文件存储配置信息 xff0c 属性中可设置输出到应用程序输出路径拷贝 xff0c 程序中读写使用 xff0c 程序修改只需修改xml文件然后覆盖到输出路径中 2 AppConfig文件存储 xff0c 可设置属性输出路径 xff0
  • 计算机大端模式和小端模式 内存对齐问题(sizeof)

    目录 43 一大端模式和小端模式的起源二什么是大端和小端三数组在大端小端情况下的存储四为什么会有大小端模式之分呢五如何判断机器的字节序内存对齐问题 再讲讲pragma pack 内存对齐二 一 大端模式和小端模式的起源 关于大端小端名词的由
  • linux虚拟机关机、重启命令

    一 shutdown 1 shutdown h now xff1a 立即关机 2 shutdown h 10 53 xff1a 到10 53关机 3 shutdown h 43 10 xff1a 10分钟后自动关机 4 shutdown r
  • C#函数的重载

    函数名必须相同方能构成函数重载 函数返回值类型 可以相同 也可以不同 注意 函数的返回类型不足以区分两个重载函数 函数参数类型 必须不同 函数参数个数 可以相同 可以不同 函数参数顺序 可以相同 可以不同 csharp view plain
  • 静态方法和实例化方法的本质区别

    本文章已收录于 xff1a 这是一个经常被时时提出来的问题 xff0c 很多时候我们以为理解了 懂了 xff0c 但深究一下 xff0c 我们却发现并不懂 方法是我们每天都在写得 xff0c 很多程序员大多都使用实例化方法 xff0c 而很
  • string,StringBuffer与StringBuilder的区别

    String 字符串常量 StringBuffer 字符串变量 xff08 线程安全 xff09 StringBuilder 字符串变量 xff08 非线程安全 xff09 简要的说 xff0c String 类型和 StringBuffe
  • C# xml文件的创建,修改和添加节点 。

    最近在做一个项目 xff0c 设计到xml文件的传输 xff0c 所以就研究了一下 xff0c NET Framework完全支持XML DOM模式 xff0c 但它不支持SAX模式 NET Framework支持两种不同的分析模式 xff
  • C#自定义ConfigSections节点操作

    sectiongroup 在config文件中加入以下节点 html view plain copy print lt configSections gt lt sectionGroup name 61 34 WebSiteInfo 34
  • 配置文件configSections节点使用实例      。

    configSections为自定义节点 xff0c 增加应用程序可移植性 xff0c 用于配置文件上传路径 xff0c 再深入应用可定义工厂方法需要加载创建的类 1 配置configSections节点 html view plain c
  • C#自定义ConfigSections节 操作 。

    sectiongroup 在config文件中加入以下节点 html view plain copy print lt configSections gt lt sectionGroup name 61 34 WebSiteInfo 34
  • C# 中的回车换行符 表示

    在 C 中 xff0c 我们用字符串 34 r n 34 表示回车换行符 string str 61 34 第一行 r n第二行 34 但是我们更推荐 Environment NewLine xff08 名称空间为 System xff09
  • C# Regex类详解

    using System using System Text RegularExpressions namespace MetarCommonSupport lt summary gt 通过Framwork类库中的Regex类实现了一些特殊
  • C#String.Split (string[], StringSplitOptions) 多参数分割得到数组

    public string Split string separator StringSplitOptions options 参数 separator 类型 xff1a System String 分隔此字符串中的子字符串的字符串数组 不
  • vim选中字符复制/剪切/粘贴

    问题描述 xff1a https www cnblogs com luosongchao p 3193153 html vim 中选中指定字符 xff0c 进行复制 剪切 粘贴 问题解决 xff1a 进入vim中visual模式 xff0c
  • for循环本质

    菜鸟 xff1a 为什么在for循环里重复定义变量不会报错 xff1f 如下代码1 xff0c 因为重复定义了两个变量 a xff0c 编译器报错 void main int a int a return 如下代码2 xff0c 用for循
  • 常见功能类库及功能

    BitConverter 数据转换类 Array 数组类 ComboBox 列表文本框
  • 基础概念笔记

    1 声明和定义的区别 声明 xff1a 是解释内存是什么类型 定义 xff1a 是赋值
  • C#中DataGridView控件使用大全

    c datagridview 分类 xff1a C C xff0b xff0b C DataGridView 动态添加新行 xff1a DataGridView控件在实际应用中非常实用 xff0c 特别需要表格显示数据时 可以静态绑定数据源
  • DataTable转成字符串复制到txt文本的小例子

    自己写了个DataTable转成字符串的方法 复制代码代码如下 public static string DataTableToString DataTable dt string dtstring 61 34 34 for int i 6

随机推荐

  • C#实现字符串按多个字符采用Split方法分割得到数组

    String字符串如何按多个字符采用Split方法进行分割呢 xff1f 本文提供VS2005和VS2003的实现方法 xff0c VS2005可以用下面的方法 xff1a string agentInfo 61 userInfo Attr
  • C#实现 UDP简单广播

    csharp view plain copy print 代码 Code highlighting produced by Actipro CodeHighlighter freeware http www CodeHighlighter
  • 事件委托 EventHandler 。

    事件就是当对象或类状态发生改变时 xff0c 对象或类发出的信息或通知 发出信息的对象或类称为 34 事件源 34 对事件进行处理的方法称为 34 接收者 34 通常事件源在发出状态改变信息时 它并不知道由哪个事件接收者来处理 这就需要一种
  • XML文件转换成字符串互相转换操作

    System Xml XmlDocument doc 61 new System Xml XmlDocument 新建对象 doc Load 34 filePath 34 XML文件路径 string content 61 doc Inne
  • dataSerVer操作方法总结

    using System using System Collections Generic using System Linq using System Text using System Data using System IO usin
  • ubuntu无法ping www.baidu.com问题

    1 使用ifconfig 查看ip 发现地址正常 2 查看dns ip地址正常 还是无法通ping www baidu com 后来把静态地址配置该为动态地址配置后成功
  • ToolStrip和ToolStripButton的用法

    假设我的toolstrip里面有三个toolstripbutton分别是tsp1 tsp2 tsp3依次加载 xff0c 如何设置tsp3显示在toolstrip的第一个按钮 ToolStripItem tsm 61 toolStrip1
  • c#多维数组的建立及操作 总结

    1C 如何定义和使用多维数组 不建议使用ArrayList xff0c 当数组里的元素是值类型在操作的时候会出现大量的装箱与拆箱步骤性能会损失许多 xff0c 而是应该用什么类型的元素创建什么类型的数组 xff0c 除非你的元素有交叉或不确
  • 在TreeView查找某一节点

    在TreeView 查找某一节点 xff0c 通常有两种方法 xff0c 一种是递归的 xff0c 一种不是递归 xff0c 但都是深度优先算法 其中 xff0c 非递归方法效率高些 xff0c 而递归算法要简洁一些 第一种 xff0c 递
  • C#的数据类型总结

    C 的数据类型可以分为3 类 数值类型 引用类型 指针类型 指针类型仅在不安全代码中使用 一 值类型 值类型包括简单值类型和复合型类型 简单值类型可以再细分为整数类型 字符类型 实数类型和布尔类型 xff1b 而复合类型则是简单类型的复合
  • C#中OpenFileDialog获取文件名和文件路径的常用方法.

    System IO Path GetFullPath openFileDialog1 FileName 绝对路径 System IO Path GetExtension openFileDialog1 FileName 文件扩展名 Syst
  • 操作XML 报错:根级别上的数据无效 和 给定编码中的字符无效 解决办法

    根级别上的数据无效 解决如下 private void button1 Click object sender EventArgs e try XmlDocument doc 61 new XmlDocument string file 6
  • DataGridRow的创建

    用原始datagridview的列名赋值的时候找不到列名 xff0c 用索引就可以 xff0c 不知道是怎么回事 DataGridViewRow dr 61 new DataGridViewRow dr CreateCells this d
  • 常用方法和属性列表

    BitConvert islittle 判断大小端 Array reverse 反排列数组
  • System.Windows.Forms.Timer与System.Timers.Timer的区别

    NET Framework里面提供了三种Timer xff1a System Windows Forms Timer System Timers Timer System Threading Timer VS NET 2005默认只有一个T
  • c++中scanf和printf

    xfeff xfeff scanf函数一般格式是 xff1a scanf 格式控制 输出表列 printf函数的一般格式是 printf 格式控制 输出表列 例3 4 用scanf和printf函数进行输入和输出 include lt io
  • win10无法上网,连网显示黄色三角形探号

    1 打开网络属性 2 打开无线电源开关 3 重启电脑 4 使用网络疑难解答 5 重启DHCP
  • 计算机程序的思维逻辑 (12) - 函数调用的基本原理

    xfeff xfeff 栈 上节我们介绍了函数的基本概念 xff0c 在最后我们提到了一个系统异常java lang StackOverflowError xff0c 栈溢出错误 xff0c 要理解这个错误 xff0c 我们需要理解函数调用
  • c语言中函数调用的原理

    xfeff xfeff 一 函数参数传递机制的基本理论 函数参数传递机制问题在本质上是调用函数 xff08 过程 xff09 和被调用函数 xff08 过程 xff09 在调用发生时进行通信的方法问题 基本的参数传递机制有两种 xff1a
  • 栈中函数调用原理_详解

    xfeff xfeff 函数调用是程序设计中的重要环节 xff0c 本文就函数调用的过程进行分析 一 eip ebp esp介绍 EIP xff0c EBP xff0c ESP都是系统的寄存器 xff0c 里面存储的是些地址 xff0c 我