C语言函数栈帧的创建与销毁详解

 更新时间:2022年02月21日 14:45:39   作者:tjh94512  
这篇文章主要为大家详细介绍了C语言函数栈帧的创建与销毁,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

前言

大家在学习的时候一定有以下困惑: 局部变量是怎么创建的?为什么局部变量的值是随机值?函数是怎么传参?传参的顺序是怎样的?形参与实参是什么关系?函数调用是怎么做到的?函数调用完成不是销毁了吗,如何带回的返回值?

以上这些都可以通过了解函数栈帧的创建与销毁来理解。接下来我就带大家来了解函数栈帧的创建与销毁。

本次使用的编辑器是VS2013,因为越先进的编辑器,越不容易观察到底层。好了,我们回到正题。

一、函数栈帧是什么?

1.寄存器

寄存器里有eax,ebx,ecx,edx,ebp,esp等,我们今天要重点了解的是ebp与esp这两个寄存器。

2.ebp与esp

这两个寄存器中存放的是地址,整两个地址使用来维护函数栈帧的。每一个函数调用,都要在栈区创建一个空间,这块空间叫做函数栈帧,用esp与ebp来维护。

二、函数栈帧的创建

1.代码块

#include<stdio.h>
int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("ret=%d\n", ret);
	return 0;
}

这个代码只是一个很简单的实现了加法函数,不用多说。

2.调用堆栈

接下来开始调试程序来看底层代码如何实现,首先打开调用堆栈

在这里插入图片描述

从这里可以清楚地知道,main函数之前还有两个函数在调用,所以大家平时认为的main函数就是最初的开始是错误的哦。

3.esp与ebp如何维护栈帧

在栈的使用中,是从高地址开始的。其中ebp指向栈底,esp指向栈顶。

在这里插入图片描述

main函数被调用后,程序会为main函数分配栈空间。接下来调试并右击鼠标转到反汇编。可以看到

在这里插入图片描述

这些代码的意思是

将ebp的值压栈,然后将esp赋给ebp,给esp减去0E4h的大小,之后分别将ebx,esi,edi压栈

压栈的意思是将元素放到栈顶。在上面调用堆栈可以看到main函数由__tmainCRTStartup调用。图解如下:

在这里插入图片描述

值得一提的是,在栈顶放元素是esp会自动指向新放元素的上方。

在这里插入图片描述

在这里插入图片描述


上图中我们很容易的看出在调用main函数是为main函数开辟的栈空间即栈帧,并且将esp的值给ebp,ebp和esp指向同一块空间,然后esp变小指向上面的区域,接下来将ebx,esi,edi压栈。

在这里插入图片描述

在这里插入图片描述

接下来这四句,lea是加载有效地址,从上图中,我们知道ebp指向的地址,那么edi存放的就是ebp-0E4h的地址也就是③esp处的地址,最后一句rep stos dword ptr es:[edi] 意思是从edi里面重复拷贝ecx次eax的内容。一次拷贝四个字节,dword是四个字节的意思。

在这里插入图片描述

接下来这三个汇编代码的意思就是将值放入相应的空间。至于为什么是为什么是ebp-8和ebp-20,这个和编译器有关,就不过多叙述。

在这里插入图片描述

在这里插入图片描述

由汇编代码可知,接下来就开始创建形式参数,将要传递的参数值存入eax与ecx这两个寄存器中并压入栈顶,所以创建的形式参数并不在add函数的函数栈帧哦,并且我们之前常说的形参是实参的一份临时拷贝无疑是非常正确的。

在这里插入图片描述

在这里插入图片描述

接下来开始add函数调用。call是调用的意思,后面是add函数的地址用来找到调用函数。这里值得一提的是call指令下一条指令的地址被存储来方便执行完add函数可以跳回来。

Add函数创建栈帧的过程其实和main函数一样的,先将ebp压栈方便找到指向main函数栈底的空间,再将esp的地址存放到ebp里面,此时,esp和ebp指向同一位置,再将esp上移0CCh个位置,然后就是ebx,esi,edi压栈,这里不多作说明,由下图可清晰理解:

在这里插入图片描述

在这里插入图片描述

接下来初始化z与执行加法也没什么要说的,z=x=y是找到之前创建的形参来进行加法。并将结果移动到z里。

在这里插入图片描述

返回z的操作是将z的值存放到eax这个寄存器中,所以函数销毁变量与返回的值这个操作并不冲突。

在这里插入图片描述

最后销毁add函数栈帧,pop就是弹出元素然后把元素放入后面的寄存器,每次pop都会销毁一个栈顶元素然后esp自动++往下挪移。三下pop之后要回收空间了,操作是把ebp的值传给esp这样esp指向的空间是main函数的栈顶,然后popebp这个操作会将之前栈底存放的之前存放的main函数的栈底指针传给ebp,这样ebp就指向了main函数的栈底。如下图:

在这里插入图片描述

接下来销毁main函数的操作也与之前一样,就不细说了。

总结 

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注脚本之家的更多内容!        

相关文章

  • C语言开发实现通讯录管理系统

    C语言开发实现通讯录管理系统

    这篇文章主要为大家详细介绍了C语言开发实现通讯录管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • 用C语言实现井字棋游戏代码

    用C语言实现井字棋游戏代码

    大家好,本篇文章主要讲的是用C语言实现井字棋游戏代码,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • C中实现矩阵乘法的一种高效的方法

    C中实现矩阵乘法的一种高效的方法

    本篇文章介绍了,在C中实现矩阵乘法的一种高效的方法。需要的朋友参考下
    2013-05-05
  • 在C语言中输入中文字符串讲解

    在C语言中输入中文字符串讲解

    这篇文章主要介绍了在C语言中输入中文字符串讲解,本文通过概念和案例相结合讲述了如何在C语言中使用中文,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • 简单掌握桶排序算法及C++版的代码实现

    简单掌握桶排序算法及C++版的代码实现

    桶排序是将要排序的算法按桶分组排序之后再遍历汇总的一种线性排序算法,下面就让我们来通过小例子简单掌握桶排序算法及C++版的代码实现^^
    2016-07-07
  • c语言操作文本的基本使用方法

    c语言操作文本的基本使用方法

    这篇文章主要介绍了c语言操作文本的基本使用方法,需要的朋友可以参考下
    2014-04-04
  • 深入理解Qt信号槽机制

    深入理解Qt信号槽机制

    信号槽是 Qt 框架引以为豪的机制之一。本文主要介绍了Qt信号槽机制,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-06-06
  • C语言详细讲解if语句与switch语句的用法

    C语言详细讲解if语句与switch语句的用法

    用 if 语句可以构成分支结构,它根据给的条件进行判定,以决定执行哪个分支程序段,C 语言中还有另外一种分支语句,就是 switch 语句
    2022-05-05
  • C++17结构化绑定的实现

    C++17结构化绑定的实现

    这篇文章主要介绍了C++17结构化绑定的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • C语言动态规划之背包问题详解

    C语言动态规划之背包问题详解

    这篇文章主要介绍了C语言动态规划之背包问题详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04

最新评论