前些阵子学的内容,拿出来作为复习,写一篇博客

一. 引入
前期学习c语言的时候,我们总是会有些困惑
-
局部变量是如何创建的?
-
为什么局部变量不初始化内容是随机的?
-
函数调用时参数时如何传递的?
-
传参的顺序是怎样的?
-
函数的形参和实参分别是怎样实例化的?
-
函数的返回值是如何带回的?
这些问题,当知道了函数的栈帧和销毁之后就可以解答了。
目录
二. 正文开始
2.1 使用的环境
对应函数栈帧的创建和销毁,不同编译器实现的过程不同,但大体逻辑是一样的
使用的环境是VS2013,不要使用太高级的编译器,越高级的编译器,越不容易学习和观察。
同时在不同的编译器下,函数调用过程中栈帧的创建是略有差异的,具体细节取决于编译器的实现。
2.2 寄存器的介绍

正如我笔记所写,eax 到 edx 都是一些普通的寄存器,用来存放值,不过多介绍
但是 下面的 ebp esp 就 稍稍不同了
ebp 栈低指针
esp 栈高指针
下面将用一张图片来引入

2.3 函数栈帧的创建
实际上,程序在运行的时候,main 函数并不是第一个运行的,它也是被其它函数调用的,如果你转到汇编的代码中,你就会发现main函数的调用前还有两个函数

这几个函数是一层层调用的,最终才到main函数

在main 函数的下方 也就是 我上述给的 函数的最上方,便是 寄存器 ebp 所在的位置,程序就从这里开始
每一个函数的创建都需要在栈区创建一个空间,那么这个空间的创建与 ebp esp 有很大的关系
2.3.1 main 函数的创建

1. 首先在这个函数的上方 push 压栈 将ebp的值压在main函数的前置函数栈空间的上方
2.mov 将 esp的 值 放到刚刚创建的压栈空间中,
3. sub 因为栈区空间的创建从高地址到低地址,这里将 esp 的值减去 0E4H 相当于创建了空间

4.3个push,压入三个值,esp到了最顶。这三个压入的元素后续会弹出,不是很重要

5.这里就是相对重要的部分,将main函数栈顶的地址放入edi中
6.然后将从edi开始的39h的内容,以dword(double word)一个word两个字节,也就是4个字节的形式,用eax的内容进行替换,这就是这段的内容,最后的效果如下
ecx 放入的内容,在创建栈帧空间时,被用作一种计数器,用ecx的值的次数,放入ecx次eax的值
每个编译器里面放的内容不同,但总是会像这样对栈帧空间放东西进去,这就是为什么变量在使用的时候要进行初始化
2.3.2 main函数中变量的创建


注意看这里,a 是在ebp-8 的位置创建, 而 b 是在 ebp - 14h 的位置创建,隔了两个空间
2.3.3 函数的栈帧空间的创建
现在,我们说完了main函数的创建,那么现在来说一下函数栈帧的创建

注意一下左边的汇编代码
1.在函数的创建的首先,它将ebp-14h,ebp-8,也就是前面a,b变量放入eax,ecx中
然后将它们压到main函数的上面,这实际上是拷贝了前面的变量
2.随后它用了一条call指令,将call指令的下一条代码的地址放在ecx上,这有大用,后面将会讲到
3.call指令之后,进入函数的创建

4.进入函数的汇编代码,这里的代码类似main函数的栈帧创建,不过多说明了

2.3.3 函数功能的实现
然后这里有好几条汇编代码,
1.
z = x + y
1.这里,先把ebp - 8 也就是刚刚拷贝的 a 的值 10 放到 eax 寄存器中
2.然后,将 ebp - 12 的值,也就是 b 的值 加到 eax 寄存器中,
3.最后,就eax的值放到刚刚创建的 z 变量中
这样,就实现了函数的功能
4. return z 由于栈帧空间使用后会被销毁,于是将 z 的值 又返回到eax寄存器当中
5.最后,将刚刚创建的各种元素pop 弹出,准备销毁空间
2.3.4 函数栈帧空间的销毁

![]()
前面的元素弹出不重要,来看到代码的的第4行
1.将ebp的值赋给 esp ,这就使得esp到了ebp的位置,也就是回收了函数的栈帧空间
2.然后pop ebp ,ebp往下一格,到了ebp - main 的元素中,自然地回到了main函数的栈底
3.然后 ret,从之前存下的下一行的代码地址 回到了主函数中
2.3.5 形参的销毁,以及返回值的去处


1.将esp 加 8 ,相当于把形参给销毁掉,最后又回归到最初的形态
2.返回值 从 eax 中 放入之前创建的 变量 c 中,至此结束了整个函数

三.解答开头的问题

至此,完。
感谢观看
2万+

被折叠的 条评论
为什么被折叠?



