函数的调用过程,栈帧的创建和销毁

在c程序中当计算机遇到某一个函数时,它就会跳转过去执行这个函数,执行完毕后接着再去执行下一条函数(指令)。在执行调用函数的过程中,计算机通常还要根据函数完成一些工作,比如数据的传递(形参拷贝、返回值)、内存的分配和释放、执行控制的转移,这些操作通过形成一个栈帧来完成。

一.什么是栈帧?
引用百度百科的经典解释:“栈帧也叫过程活动记录,是编译器用来实现函数调用过程的一种数据结构。” 实际上,可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。让我们从栈开始来理解什么是栈帧… 栈作为一种特殊的数据结构而存在(和“队列”相反的记录结构和操作规则),是一种只能在一端进行插入和删除操作的特殊线性表。栈按照后进先出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。 栈有很多自己的特性,它具有记忆功能,对栈的插入与删除操作中,不需要改变栈底指针;而且栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。因此栈作用就是用来保持栈帧的活动记录(即函数调用)。
二、函数调用每一次函数调用都是一个过程这个过程我们称为函数的调用过程,这个过程要为函数开辟栈空间,用于本次函数的调用中临时变量的保存,现场保护。这块栈空间我们称为函数栈帧。栈帧的维护必须先了解两个寄存器。 寄存器ebp称为“基址指针”,在未受改变之前始终指向栈底,用途是:在堆栈中寻址。 寄存器esp称为“栈指针”,会随着数据的入栈出栈移动,也就是说始终指向栈 顶。还有几个通用寄存器:EAX,EBX,ECX,EDX.EIP(PC).其中EIP(PC)是程序计数器,用来保存当前正在执行的指令的下一条指令地址。当我们要详细研究函数调用过程,必须得对照的汇编代码。 从main函数的地方开始,要展开main函数的调用就得为main函数创建栈帧。
在这里插入图片描述

栈帧的形成通过栈指针esp和帧指针ebp的移动及相关操作来完成。
在介绍具体调用过程之前先对相关的几个名词作一解释。
1、ebp:栈底指针,即指向栈帧底部的指针。
2、esp:栈顶指针,栈帧指针只有一对。
3、call:用于保存当前指令的下一条指令并跳转到目标函数。
3、push:入栈(push 寄存器 保护数据)。
4、pop:出栈。
5、ptr:相当于指针,详解:汇编语言中的ptr(很全)
6、mov:类似于赋值操作。
7、add:加法操作。
8、sub:减法操作。
让我们通过下面这段代码来看一看栈帧具体是如何形成并完成相关功能的。

#include <stdio.h>
#include <windows.h>

int fun( int x, int y)
{
	int c = 8;

	return c;
}

int main()
{
	int a = 2;
	int b = 4;

	int ret = fun(a, b);

	printf("you should run here!\n");

	system("pause");
	return 0;
}

下面是main函数调用fun函数之前进行的一些操作,包括这些:
1、参数拷贝(参数实例化)。
2、保存当前指令的下一条指令,并跳转到被调函数。
这些操作均在main中进行。

在这里插入图片描述
接下来是调用fun函数并执行的一些操作,包括:
1、移动ebp、esp形成新的栈帧结构。
2、压栈(push)形成临时变量并执行相关操作。
3、return一个值。
这些操作在fun中进行。
在这里插入图片描述
被调函数完成相关操作后需返回到原函数中执行下一条指令,操作如下:
1、出栈(pop)。
2、回复main函数的栈帧结构。(pop )
3、返回main函数
这些操作也在fun中进行。在这里插入图片描述
至此,在main中调用fun的整个过程已经完成

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值