函数栈帧的创建和销毁!-一点点理解 慢慢深入。
加油呀! K-
以下简介本文的的主要解决问题:
1.局部变量是怎么创建的?
2.为什么局部变量的值是随机的?
3.函数怎样传参的?传参的顺序是怎样的?
4.形参和实参是什么关系?
5.函数调用是怎么做的?
6.函数调用在结束后使怎么返回的?
相信学完本文你会有些许的明了!
别划走 学习需要坚持呀 加油!
函数栈帧的创建和销毁!
小贴士-
使用的编译环境是VS2013,尽量使用不太高级的编译器,因为编译器越高级,对函数封装越好,越不利于学习和理解。
函数栈帧的创建和销毁的过程在不同的编译器下是会略有差异的,具体取决于编译器,但是大体逻辑相同。
小伙伴们站稳坐好 准备发车啦!
一、寄存器
1.寄存有如下: eax ebx ecx edx ebp esp...2.ebp esp这两个寄存器中存放的是地址,用于维护栈帧。
3.每一个函数调用,都要在栈区上创建一个空间。
二、图文详解
1.零碎tips
- 正在调用哪个函数,ebp 和 esp 所维护的便是哪个函数的函数栈帧。
- 函数的函数栈帧区域即为两个寄存器(ebp esp)之间的空间。
- ebp(栈底指针) esp(栈顶指针)
- 栈区的使用习惯:先使用高地址 再使用低地址
- 局部变量创建在 栈上
2.图文解释
1).在VS2013中,main函数也是被其他函数调用的->该函数为*__tmainCRTStartup* -> 而其又被 mainCRTStartup 调用
如图所示:首先:main函数被调用 其次__tmainCRTStarup函数被调用 最后mainCRTStarup函数被调用。
再加上下图所示:可得出–> 在VS2013中,main函数也是被其他函数调用的->该函数为*__tmainCRTStartup* -> 而其又被 mainCRTStartup 调用。
2)在 Fn +F10 之后 右击鼠标选择“转到反汇编”-(右击or查看选项)显示地址、显示源代码
(对照反汇编-作出图)
反汇编涉及的字符含义:
- push-压栈
- move-把后面的值赋给前面
- sub-前面减去后面
- lea-load effective address 加载有效地址:把后面的有效地址加载到前面(若地址不好观察 则打开 显示符号名)
- dword-double word 四字节的数据
- 压栈:从栈顶放入元素 push
- 出栈:从栈顶拿出or删除数据 pop
- call-调用 (按Fn+F11) -一定要记住call的地址
3)大致图及注意点
3.实例+图文分析
//VS2013
#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\n", c);
return 0;
}
三、问题复盘
(其实答案均在上文图中可以找到!)
1.局部变量是怎么创建的?
首先为函数分配好栈帧空间,初始化好栈帧空间,然后在栈帧空间中为局部变量分配一点空间。
2.为什么局部变量的值是随机的?
因为随机值是在创建栈帧空间过程中随机存放的,若局部变量未初始化,值便会是创建栈帧时所储存的随机值。初始化局部变量则会将随机值覆盖。
3.函数怎样传参的?传参的顺序是怎样的?
还没调用时就已经用push 将实参的值从右向左(即:后出现的先压栈)压入栈中,在调用函数时形参找到的值是压栈中的值。
4.形参和实参是什么关系?
形参是在压栈时开辟的空间,是实参的一份临时拷贝,二者的值相同,但是空间是相互独立的,故 实参的值改变不影响形参。
5.函数调用是怎么做的?
如图!
6.函数调用在结束后使怎么返回的?
call指令的下一指令的地址被存储,当call指令执行完毕pop后回到main函数就可以找到call的下一指令,接着执行。而函数的返回值又被存储在寄存器中,不随函数空间的释放而释放,最后将寄存器储存的值最终返回。
总结
函数栈帧确实很难 但要好好理解 再认真复盘呐!
one day!
K-加油!