函数栈帧的创建和销毁

前言

   函数是c语言的基本模块,我们编写的c语言程序都是由一个或多个函数组成的,而当我们调用一个函数时是需要在栈区中开辟一段空间的,那么这段空间是如何开辟的,函数调用结束后又是如何返回上一级函数的呢?

   首先我们先编写一段简单的代码,是用函数实现两数求和。

   代码如下:代码的实现是在vs2013中实现的

#include <stdio.h>

int Add(int i_number_one, int i_number_two); //求和函数的声明

int main(void)
{
	//声明两个变量
	int i_number_one = 20;
	int i_number_two = 30;
	int i_sum = 0;

	//调用求和函数
	i_sum = Add(i_number_one, i_number_two);

	//打印结果
	printf("%d", i_sum);
	getchar();                //代码的实现是在vs2013中实现的,getchar()为了让程序运行框不是 
                              //一闪而过
	return  0;
}

//Add函数的实现
int Add(int i_number_one, int i_number_two)
{
	int i_sum = 0;
	i_sum = i_number_one + i_number_two;

	return i_sum;
}

我们用F10打开程序调试,然后在打开反汇编窗口和调用堆栈窗口,这两个窗口在vs的菜单栏中的调试->窗口下可以选择

调用堆栈的功能是用来记录函数的调用过程,从中我们会发现当程序在执行main函数时已经运行了很多的代码,其实这是c语言编译器内置的代码,是运行程序必备的,从中我们也可以得出一个结论,main函数也是一个被调用的函数

 

 我们都知道我们编写的c程序在开始运行的时候,要进行 预处理 -> 编译 -> 反汇编 -> 链接 这四个过程,而反汇编窗口顾名思义就是对程序反汇编过程的显性体现。

而我们今天的关键问题-函数栈帧的创建与销毁是如何进行的,通过我们对反汇编代码的解读就会体现出来! 

1.ebp和esp的作用

 我们可以看到程序运行的第一个汇编指令为 push ebp,是将通用寄存器中的 bp 寄存器压栈 ,然后就是 mov ebp,esp 是将sp寄存器中的值传给bp寄存器,这时我们就会有疑问,为什么c程序开始运行main()函数时要先使用两个寄存器?这两个寄存器是干什么用的呢?

 

 函数的调用是要在栈区开辟一段空间的,而这段空间是通过esp和ebp这两个寄存器来维护,ebp中存放这段空间的栈底的地址,而esp是用来存放栈顶的的地址。

  我们知道c程序的开始运行是从main函数开始的,那为什么我们在程序开始运行的时候,反而先压栈了一个栈底地址?(向栈空间中压入了一个栈底的地址)

 这也恰恰验证了c语言中main函数其实也是一个被调用的函数,当main运行完了后还要返回到调用main()函数的函数中。

所以我们的程序的栈区示意图应该为

开始调用main函数前:

开始调用main函数后:

然后是mov ebp,esp将esp此时的地址放到ebp寄存器中,此时ebp就和esp指向同一个地址,这个地址就是main函数栈空间的栈底空间。

 然后后面的汇编代码为:

 sub可以类比为c语言中的减法 ‘-’ 同样操作数都为两个:

sub         esp,0E4h

这句话的意思是相当于: esp = esp - 0E4H(十六进制数)                                                               可以理解为将esp向上移动数个字节的空间。

 然后是

将ebx esi edi压入栈中,每次压栈esp就会减1,

然后就是

 

将ebp-0E4h这段空间的起始地址存入edi中,ecx中保存的是这段空间的字节个数,eax中保存的是对空间初始化的的值这个是随机值,编译器不同值不同,然后开始将从edi保存的起始地址开始向下39h个字节初始化为eax中的值。

通过调试窗口中的内存我们可以清楚的看到这个过程

然后就是: 

 

 然后就是:

 

在main函数栈帧的上边开辟空间放两个参数的值

 

这汇编代码是记录下一条代码的值

然后程序的执行就会进入到Add函数中

 这里Add函数的开辟栈空间的方法同main函数一样,不在过多的赘述

主要看Add函数的销毁

 首先将想要return回main的函数放到eax中,然后将edi,esi,ebx出栈,每次出栈esp要减1,然后将Add函数的栈底地址给esp,然后Pop ebp,此时程序为Add函数所开辟的栈帧空间释放了,然后将之前记录的main函数的栈底空间,给ebp,ret是子程序返回指令,回到存储的下一条指令的地址的位置。

 

然后再将esp加8,意思也就是让esp指向main的空间中的edi的地址,此时程序的执行返回main函数中,开始执行其他剩余的代码,main的栈空间的销毁同Add函数的相同

此时我们也就明白了,函数栈帧的创建与销毁了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值