【c语言技能树】函数的创建与销毁 --函数栈帧

本文详细介绍了C/C++编程中函数栈帧的创建与销毁过程,从基础栈帧模型到函数调用的实际操作,包括Main函数和Add函数的栈帧分析,以及LeetCode刷题的实践应用。文章通过汇编指令解释了局部变量的存储、函数调用机制和返回值的处理,展示了函数生命周期中的内存管理和控制流程。
摘要由CSDN通过智能技术生成

Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法......感兴趣就关注我吧!你定不会失望。

🌈个人主页:主页链接

🌈算法专栏:专栏链接

     我会一直往里填充内容哒!

🌈LeetCode专栏:专栏链接 

    目前在刷初级算法的LeetBook 。若每日一题当中有力所能及的题目,也会当天做完发出

🌈代码仓库:Gitee链接

🌈点击关注=收获更多优质内容🌈

 本章内容记录下函数的创建与销毁,也即函数栈帧的内容

目录

0.相关知识

1.基础栈帧模型

2.Main函数的函数栈帧

 3.Main函数代码的运行

4.Add函数的函数栈帧

 5.return->Main函数

 至此,完成了函数的创建与销毁,感谢观看!

 

0.相关知识

以下内容可能乍一看有点费解,但在我讲的过程中再看就很容易理解啦,

eax:通用寄存器,保留临时数据,常用于返回值
ebx:通用寄存器,保留临时数据
ebp:栈底寄存器
esp:栈顶寄存器
eip:指令寄存器,保存当前指令的下一条指令的地址

mov:数据转移指令
push:数据入栈,同时esp栈顶寄存器也要发生改变
pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
sub:减法命令
add:加法命令
call:函数调用,1. 压入返回地址 2. 转入目标函数
jump:通过修改eip,转入目标函数,进行调用
ret:恢复返回地址,压入eip,类似pop eip命令

1.基础栈帧模型

在操作系统中,栈总是自上而下增长的,在x86-64的环境下,栈顶由Esp进行定位.

如图,这是一个基础的栈帧模型

每一次函数的调用,就要为本次函数调用开辟一段空间,这就是函数栈帧空间.

通过esp与ebp来维护这段函数空间,ebp记录的是栈底的位置,而esp则记录栈顶的位置

但需要注意:

esp相对于ebp来说,地址较低.

执行push操作时,esp上移一段,将数据进栈

执行pop操作时,弹出数据到制定位置,同时esp下移一段

现在介绍完了基础知识,我们来看看下面这段代码在编译器中是怎么运行的吧

#include<iostream>
using namespace std;
int add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 1;
	int b = 2;
    int c = 0;
	c = add(1, 2);
	return 0;
}

2.Main函数的函数栈帧

只要你学过c语言就一定知道,代码是从main函数开始运行的.也就是若要创建其他函数的函数栈帧,是从main函数中创建的.

那么谁来创建main的函数栈帧呢?

 我们打开vs2022来看看

发现在调用main函数之前就先调用了invoke_main()这个函数 但我们不深究这个函数,我们只需要知道,main函数也是被其他函数调用产生的就可以了.所以invoke_main也会有自己的函数栈帧.

接下来我们来看看如何初始化Main的函数栈帧,打开反汇编指令

00BE1820 push ebp
00BE1821 mov ebp,esp
00BE1823 sub esp,0E4h
00BE1829 push ebx
00BE182A push esi
00BE182B push edi

第一步 将ebp(维持栈底空间的寄存器)加入栈帧

第二步 将invoke_main函数的esp移动到现在ebp的位置

第三步 将esp减去也就是上移0E4h的空间,也就是十进制下的228,这个操作就使得ebp与esp之间维护了一段栈帧空间

第三步 将ebx esi edi三个寄存器插入到栈帧空间中,同时push操作会使esp继续上移

 

00BE182C lea edi,[ebp-24h]
00BE182F mov ecx,9
00BE1834 mov eax,0CCCCCCCCh
00BE1839 rep stos dword ptr es:[edi]

 上面三步使得栈空间中维护了一段空间,接下来需要将空间初始化.

        在edi中放入ebp-24h的值,也就是ebp的值

        将9放入ecx

        在eax中放入初始化的值0cccccccch

        之后将esp到edi(ebp)之间的值都给上cccccccc

 到此完成了main栈帧的初始化,可以开始运行代码了!(这里也说明为什么数据不初始化直接进行打印会是烫烫烫烫烫 因为这就是操作系统分配的随机值)

 3.Main函数代码的运行

首先看看我们的代码

	int a = 3;
	int b = 5;
    int c = 0;    

这两步对应到汇编代码中就是

00BE183B mov dword ptr [ebp-8],3
00BE1842 mov dword ptr [ebp-14h],5
00BE1849 mov dword ptr [ebp-20h],0

        在ebp-8的位置与ebp-12 ebp-16的位置放上3,5与0

完成对变量abc的初始化(这里也说明局部变量是存在栈上的)

之后开始执行这段代码,涉及到了新函数的开辟.

c = add(a, b);

 首先回忆刚刚Main函数的创建过程.有了一个新的函数我们肯定需要创建一个新的栈帧

00BE1850 mov eax,dword ptr [ebp-14h]
00BE1853 push eax
00BE1854 mov ecx,dword ptr [ebp-8]
00BE1857 push ecx

因为这里用到了a,b这两个变量.

所以我们需要先将a,b拷贝到出来 不然待会要用的时候访问不到.

        将ebp-12的地方的值(b)拷贝出来,放入eax中,将eax进行压栈处理

        将ebp-8的地方的值(a)拷贝出来,放入ecx中,将ecx进行压栈处理

 之后进行调用函数的处理!

00BE1858 call 00BE185D
00BE185D add esp,8

           call 将原本主函数中要执行的下一条指令地址进行压栈,因为现在不是在进行Add函数的创建吗?之后再跳回来的时候能找到下一条指令的地址(也就是add 的这一条指令) 执行完call,我们要正式进入Add的创建

4.Add函数的函数栈帧

与Main函数栈帧创建大致相同,我们再来复习

0BE1760 push ebp 

        记录下main函数的ebp放在什么位置,将其压栈

00BE1761 mov ebp,esp 
00BE1763 sub esp,0CCh 
00BE1769 push ebx 
00BE176A push esi 
00BE176B push edi 

        创建大小为0cch的空间,其余操作与Main函数创建一样就不细讲了

 

00BE176C mov dword ptr [ebp-8],0 
00BE1773 mov eax,dword ptr [ebp+8] 
00BE1776 add eax,dword ptr [ebp+0Ch] 
00BE1779 mov dword ptr [ebp-8],eax 

         之后在ebp-8的位置创建变量z赋值为0

         将刚刚的那两个变量放入到a` b`放入到eax中完成相加

        再将其放入到z中

 

00BE177C mov eax,dword ptr [ebp-8]

        将z的值放入到eax寄存器当中,因为临时变量会被销毁,而存储到寄存器中的值不会被销毁

00BE177F pop edi 
00BE1780 pop esi 
00BE1781 pop ebx  

        最开始有提到过,pop操作就是将其出栈,并将这个元素的值放入到对应的寄存器当中.

00BE1782 mov esp,ebp
00BE1784 pop ebp
00BE1785 ret

        这里是将ebp的值先放到esp中,这样就等于收回了Add函数的栈帧,之后弹出ebp的值,

所以此时ebp又回到了Main函数的位置,

        ret指令就是弹出栈顶刚刚call存入的指令地址,并执行.然后pop

 5.return->Main函数

00BE185D add esp,8
00BE1860 mov dword ptr [ebp-20h],eax

        将esp+8也就是将a` b`弹出栈,之后将eax中的值放回到c中

 至此,完成了函数的创建与销毁,感谢观看!

🌈本篇博客的内容【函数的创建与销毁 --函数栈帧】已经结束。

🌈若对你有些许帮助,可以点赞、关注、评论支持下博主,你的支持将是我前进路上最大的动力。

🌈若以上内容有任何问题,欢迎在评论区指出。若对以上内容有任何不解,都可私信评论询问。

🌈诸君,山顶见!

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ppeua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值