C | 函数栈帧的创建与销毁
目录
- 前言
- 函数栈帧的创建与销毁
-
- 寄存器
- 函数栈帧
- 内存使用示意图
- 被调用的main函数
- 一些反汇编代码的解释
- 过程分析
- 结语
前言 在我们刚开始学习C语言的时候,相信小伙伴们和我一样有很多的困惑吧?
比如:
局部变量是怎么创建的?别着急,今天就让我们一起来学习一下函数栈帧的创建与销毁。
为什么局部变量的初始值是随机值?
函数是怎么传参的?
函数传参的顺序是怎么样?
形参和实参有什么关系?
为什么说形参是实参的一份临时拷贝?
函数调用的过程是怎么实现的?
函数调用结束后是怎么返回的?
相信通过今天的内容,小伙伴们对以上的困惑会得到答案。
重要的话放在前面:由于我所了解到的知识还不够深入,难免会有说的不当的地方,如有说错,请各位大佬指出!
函数栈帧的创建与销毁 寄存器 首先,我们需要了解一个概念:寄存器。
常见的寄存器有:
eax寄存器并不是在内存中的,而是集成在CPU中,函数栈帧的创建与销毁和寄存器有密切的关系。
ebx
ecx
edx
ebp
esp
函数栈帧 函数栈帧就是在内存的栈区中,为函数调用而开辟的一块内存空间
而ebp和esp这两个寄存器是用来存放地址的,这两个地址就是用来维护函数栈帧的。
内存使用示意图 内存的使用大致如下:
文章图片
被调用的main函数 再C程序中,我们知道main是程序的入口,一个程序只能有一个入口,但是,其实main函数也是被别的函数调用的。只不过这些过程被编译器封装起来了,我们不易观察而已。而且,版本越高的编译器越不容易观察这一过程。
以VS2013编译器为例,我们再调试的时候,可以鼠标右键进入反汇编,然后不断按下F10,看程序的执行过程。
当我们执行到main函数最后一行的时候,再按F10会看到,其实mains函数是被_tmainCRTStartup这个函数调用的。
文章图片
文章图片
【C | 函数栈帧的创建与销毁】
文章图片
到这里,其实我们需要知道,main也是被别的函数调用的即可。而main函数被调用的时候,也会开辟一块属于自己的函数栈帧。
文章图片
一些反汇编代码的解释 mov :数据传送指令,把数据传送到指定的地址,从右往左传送
sub:减法指令,把地址进行减法运算
push:实现压入操作的 (压栈,即给栈顶放入一个元素)
pop:实现弹出操作的指令(出栈,即给栈顶拿出一个元素)
call:用于保存当前指令的下一条指令并跳转到目标函数
过程分析 我们已下面代码为例,来分析函数栈帧的创建与销毁的过程。
#include
int Add(int a, int b)
{ int c = 0;
c = a + b;
return c;
}int main()
{ int a = 15;
int b = 25;
int c = Add(a, b);
printf("%d", c);
return 0;
}
esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。 (栈顶指针)
bp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。 (栈底指针)
由于每次开辟新的栈帧都是使用低地址,而这一过程再内存图中形象的成为”压栈“。
文章图片
文章图片
文章图片
函数栈帧的创建,主要就是ebp和esp这两个指针压栈而形成,当这两个指针出栈的时候,函数栈帧也就销毁了。
当函数传参的时候,是在调用函数前就已经为参数开辟好空间,所以说形参是实参的一份临时拷贝。
函数的返回值是通过寄存器的方式返回的的。
当调用另外的函数的时候,寄存器会记录下当前的位置,并让ebp和esp指向新的空间。
而这基本就是函数栈帧的创建与销毁的过程。
结语 其实这些知识也是我不太懂的,也是希望写一篇博客来加深我对这些知识的理解,如果有错误的地方,还希望大佬们指出!!
最后,创作这篇文章太不容易啦,我也是找了很多资料
希望大伙儿可以点个关注、给一个赞在给一个评论哦~
十分感谢大家的支持!!
文章图片
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量