函数栈帧的创建和销毁(详细理解)

🎁个人主页:我们的五年

🔍系列专栏:c语言课程学习

🎉欢迎大家点赞👍评论📝收藏⭐文章

目录

问题:

1.ebp,esp两个寄存器用来维护函数栈帧

2.main函数也一个函数,main函数也要被其他函数调用

3.函数栈帧创建的过程

4.函数栈帧的销毁过程


问题:

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

调用函数的时候,会为函数开辟一块空间,然后第一个局部变量从栈低分配一块空间给局部变量。
2.为什么局部变量的值是随机的?

因为在为函数开辟空间的时候,这块空间的里的内存都被初始化为一个值,不同编译器中值可能不同。如果只是创建局部变量,没有初始化覆盖里面的值,那此时局部变量里的值就是为函数内存初始化的值。
3.函数是怎么传参的?传参的顺序是怎样的?

调用函数的时候,会在该函数的栈顶压栈,该内存里放的是形参,然后通过ebp的偏移量就可以找到形参。 传参的顺序是从右到左。
4.形参和实参是什么关系?

形参是实参的零时拷贝,形参是在函数栈顶新开辟的一块空间,这块空间与实参的空间相互独立,只是数值一样。改变形参不会改变实参。
5.函数调用是怎么做的?

函数会把函数的下一指令的地址保存在call中,并且保存ebp的位置。

然后为新的函数开辟空间,在新的函数的栈顶压入三个寄存器ebx,esi,edi,然后新的空间全部初始化为一个值。然后就执行新新函数里的指令。
6.函数调用以后是怎么返回的?

返回值是通过寄存器带回来的,寄存器是全局的,不会因为函数的销毁而销毁。

然后根据保存的ebp,和下一指令的地址找到新的函数,即要执行的指令的地址。
不同的函数会开辟不同的空间。

1.ebp,esp两个寄存器用来维护函数栈帧

1.ebp寄存器:栈底寄存器。

2.esp寄存器:栈顶寄存器。

3.pc指针寄存器:也叫程序计数器,它永远指向当前指令的下一条指令。

当前有下面代码:

cpp 复制代码
#define CRT_SECURE_NO_WARNINGS 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 c = 0;
	c = Add(a, b);
	printf("%d", c);
	return 0;
}

●这个程序中,我们先进入main函数,esp寄存器和ebp寄存器就用来维护main函数的函数栈帧。当指令到达c=Add(a,b);的时候,就要调用Add函数,这时候,esp寄存器和ebp寄存器就要区维护Add函数的函数栈帧。

●每一个函数都有自己的ebp和esp,即每个函数都有自己的栈顶和栈底。

●在CPU中,ebp和esp不会有很多,但是函数却有很多。但是程序运行的时候,不可能一边运行两个函数,当运行main函数的时候,ebp和esp去维护main函数的函数栈帧。当进入Add函数,去运行Add函数的时候,esp和ebp就去维护Add函数的函数栈帧,并且把mian函数的esp和ebp的指针保存下来,等Add函数结束以后,esp和ebp就又可以去维护main函数的函数栈帧。

●地址的使用顺序是从高地址,到低地址。

2.main函数也一个函数,main函数也要被其他函数调用

当main函数结束以后,函数栈帧回到__tmianCRTStartup()。

●由此可知main函数被__tmainCRTStartup()调用。

●__tmainCRTStartup()函数被mainCRTStartup()函数调用。

程序先进入mainCRTStartup函数,为mainCRTStartup函数开辟函数栈帧,然后调用__tmainCRTStartup函数,为__tmainCRTStartup函数开辟函数栈帧。所以上面的图中比main高地址处还有这两个函数的函数栈帧,这两个函数的函数栈帧比main函数的函数栈帧高。

3.函数栈帧创建的过程

在调用main函数时,已经存在来到下面情形,__tmainCRTStartup()调用main函数

🚗1.push ebp:在栈顶,在栈顶把ebp指针的值放进去。esp就要改变位置,往上移一点,栈顶改变,esp也要改变。指针的大小是四个字节,在栈顶存ebp的值,所以就开辟4个字节的空间,里面放着ebp的值。

🚗 2.mov ebp,esp:把esp的值给ebp,那么ebp就指向esp指向的地方。

🚗3.sub esp,0E4H:0E4H是一个16进制数,转化为10进制是228.让esp减小228。减小是往上增长,往低地址处变化。

🚗4.push ebx

push esi

push edi:

在栈顶压入ebx,esi,edi,esp跟着变化。

🚗5.lea=lode effective address

把edi往下的到ebp内存里的值全部变为0cccccccch.word是两个字节,dword是double word占4个字节。

🚗6显示地址以后:

🚗7.Add函数调用


[ebp+8]找到a,[ebp+0ch]找到b,b给eax,然后eax+(add)a,然后eax给z。

Add函数中没有创建形参a,b。

4.函数栈帧的销毁过程

【ebp-8】表示z,也就是把z的值给寄存器eax,这样z的值就不会丢失。

三次pop销毁edi,esi,ebx。

ebp的值给esp,esp就指向ebp指向的位置。

然后pop ebp,把ebp指向空间的值给ebp也就是main ebp。

然后ret call的下一条指令的地址。使程序可以从main函数调用完Add函数以后,执行Add函数的下一条指令。

然后把main函数的指令执行完。

最后把main函数的函数栈帧给销毁。

回到__tmainCRTStartup函数。

相关推荐
Starry_hello world1 小时前
二叉树实现
数据结构·笔记·有问必答
嵌入式AI的盲3 小时前
数组指针和指针数组
数据结构·算法
一律清风3 小时前
QT-文件创建时间修改器
c++·qt
风清扬_jd3 小时前
Chromium 如何定义一个chrome.settingsPrivate接口给前端调用c++
前端·c++·chrome
Death2004 小时前
Qt 6 相比 Qt 5 的主要提升与更新
开发语言·c++·qt·交互·数据可视化
reyas5 小时前
B树系列解析
数据结构·b树
Indigo_code5 小时前
【数据结构】【顺序表算法】 删除特定值
数据结构·算法
麻辣韭菜6 小时前
网络基础 【HTTP】
网络·c++·http
阿史大杯茶6 小时前
Codeforces Round 976 (Div. 2 ABCDE题)视频讲解
数据结构·c++·算法
Good_Starry6 小时前
Git介绍--github/gitee/gitlab使用
git·gitee·gitlab·github