函数栈帧的创建与销毁

目录

一.困惑

二.前提储备知识

三.main函数的创建

1.前提

2.代码

3.调用堆栈

4.函数帧帧的创建

4.1变量的存储

4.2调用函数

五.函数栈帧的销毁

5.1Add函数的销毁

六.总结 


一.困惑

在学习C语言中,或许有这样几种问题:

比如:

1.局部变量是怎么创建的?

2.为什么局部变量的值是随机值?

3.函数是怎么传参的?传参的顺序是怎么样的?

4.形参与实参的关系?

5.函数调用是怎么做的?

6.函数调用是结束后怎么返回的?

下面,来我们来认识函数内部原理——函数栈帧的创建与销毁

二.前提储备知识

1.函数创建与销毁都是在数据结构——栈上完成的。

2.ebpesp分别是栈低指针栈顶指针,用来维护函数栈帧。(与数据结构的头指针无关)

3.eax,ebx,ecx,edx(包括上面两个)都是寄存器。(它们都是集成到CPU上,与栈无关)

4.汇编指令:push往栈顶加一个元素(压栈) pop从栈顶移除一个元素(出栈)

mov 将元素移到那个内存里    lea(load effective address)加载有效地址(分配内存空间)

三.main函数的创建

1.前提

建议使用老的编译器进行观察(VS2013)越新的编译器(VS2022)优化的太厉害,不能观察到更多细节。在这里,用VS2022来演示(也与别的编译器进行对比)

在VS小小的设置一下,方便观察

2.代码

这里有函数来实现两数的相加(变量的创建能写有多详细就多详细)

#include<stdio.h>
int Add(int X, int Y)
{
	int tmp = 0;
	tmp = X + Y;
	return tmp;
}
int main()
{
	int M = 10;
	int N = 20;
	int A = 0;

	A = Add(M, N);

	printf("%d", A);
	return 0;
}

3.调用堆栈

在上面的代码中,我们按下(Fn+)F10来进行调试,当语句执行到最后时(即调试的箭头来到人return 0;的下面),再按下F10,程序会跳到别的函数内部

原来,再程序运行时,main函数结束后还没有停止,在接下去还在进行函数的调用。

这时,我们打开窗口的调用堆栈。

main函数不是最终的函数,它也是被其它函数调用的

4.函数帧帧的创建

在进行反汇编的调试时,所出的内存地址在一次调试时有可能不同。(以下是在不同的时间的反汇编的截图,在此声明)

 以上是通过反汇编结合栈的作图,绘制出main函数栈帧的创建

 我们也可以通过esp(栈顶指针)来证明地址是(从下往上)从高到低。

 我们也可以通过内存来观察,这里就不演示了。

4.1变量的存储

将main函数里的变量M N A进行存储。

注意:ebp-14h是十六进制,在作图时要换算成十进制。

在存储完后,call指令存储的时下一条指令(add)的地址。

注:在试行call指令(按F11)之前,先在内存里找到esp对应内存地址的位置,通过它的上一个地址来观察内存存储的数据。(这步很重要,后面的add函数调用完后下一条指令的执行在这里进来的!

先执行eax(N),再执行ecx(M)函数参数的调用顺序是从右向左实现的!

4.2调用函数

在执行call时按两次F11就跳到Add函数内部来进行。前面的步骤(画方框的指令)与main函数的创建大致相同,但在越新的编译器里,这部分被优化了,这导致我们学习的不方便,所以在学习时,建议用来一点的编译器(vs2013)来使用。

 在Add函数里,只创建了tmp变量,而在处理x,y时是通过eax与ecx来找到M,N的值。

从而能说明,形参(M,N)的确是形参(X,Y)的一份临时拷贝!

在return tmp时将tmp的数据保存在eax中。 

五.函数栈帧的销毁

5.1Add函数的销毁

销毁无非时把空间还给操作系统,那么,在内部是如何进行的呢?一起来画图就明白了。

  

 三个pop(删除栈顶元素)完后,mov esp,ebp,把esp移到ebp的位置上(销毁Add函数的空间)

pop ebp是把ebp返回到之前维护main函数的ebp的位置上(在main函数里push的ebp地址)

完成对Add函数的回收!

5.2main函数内部

执行ret指令时调到add指令上(实际是跳到call指令,但因为call存储的是add指令的地址!)

不仅要调用Add函数,还要再调用完后回到main函数的调用位置后面继续执行(对A的打印)

 整个函数栈帧的创建与销毁可以说是基于这一点来使其变得程序整个的严谨!

接下来,对main函数的销毁与调用main函数的返回大都与前面差不多。这里就不展开赘述了。

看完大概内部原理后,我们在开始提出的问题基本上都有答案了。

在上面的困惑中,为什么会在程序里打印“烫烫烫”的 问题,相信你也有答案了,在栈上空间存着CCCCCCCC的字样,其实就是我们没对这里面进行变量的赋值导致的

六.总结 

了解函数栈帧的创建与销毁这个内部原理,对于我们了解程序内部变得更深刻。之前在学习上的困惑恍然大悟:原来是这样!

这部分的内容仅仅是给我们认识计算机内部原理的冰山一角。但在我看来,原来学习计算机会有这般有趣!使我更加期待进一步的学习!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值