目录
我们平时在使用函数时编译器是怎么工作的?内存是怎么处理的呢?使用的时候从来没有探究过这个底层逻辑,今天我们就从底层来讲解函数栈帧的创建和销毁吧。带领大家一起来探讨原理。首先我们先写一个简单的代码,如右图所示,创建了一个Add函数,然后main函数调用了他。
在了解这个之前,我们先了解一个东西:寄存器,esp和ebp,这两个是维护我们函数的栈帧的,我们后面要频繁地用到的。我们函数调用时都要在栈开辟空间。
esp是栈顶指针,ebp是栈底指针, 这时我们想看看底层是怎么运行的,我们可以按下F10键,然后右击鼠标,点转到反汇编,我们就可以看到了,如下图所示:
理解代码
首先,我们要调用main函数,我们要用到__tmainCRTStart函数,首先为这个函数开辟一个空间,
下面是高地址,上面是低地址 ,我们看上面的反汇编代码,首先push圧栈,圧栈时esp会自动减小地址
接下来是mov 和sub,将esp放入ebp,esp地址减小,移向低地址,就会发生这样的事情,就是main函数的函数栈帧。
然后又圧栈了三个进去,分别是ebx,esi,edi三个,
接下来的三行:
这三行的总体内容就是从edi位置开始,向下的39h个地址全部初始化成 0CCCCCCCh。
这样之后才开始执行有效代码,有了函数栈帧之后我们要在函数栈帧之内找到一些空间,将这些创建的变量创建进去,
就是这样创建的。
然后我们就开始调用函数
第一个mov就是让b参数让放入eax,然后将他圧栈,再然后将a的参数放入ecx,然后圧栈。下一条call指令就是将call指令的下一条指令圧栈,让他执行完之后继续执行call指令的下一条指令。记住,这些都是在为开辟add函数栈帧做准备。
以上就是在开辟add栈帧,其实差不多就是在做之前开辟main函数差不多的事情。
这就是开辟add函数的情况 ,然后就是执行函数里面的内容了。但是我们传过来的参数怎么执行呢?
整体意思就是我们之前语句进行的传a和传b,通过ebp去找到他们,将他们加起来之后存储到add函数里面去。 所以我们通常说,形参是实参的一份临时拷贝。这句话说的很正确。然后将结果返回,但是出函数之后函数就销毁了,我们将结果放到了寄存器里面,然后再销毁的,这样就不会丢了。
最后就是销毁了,
先把前面圧栈进去的pop掉,然后将中间这一段空间,全部释放掉, 然后esp和ebp会回到维护main函数的栈帧的地方,完美闭环。add函数怎么销毁的,那么main函数就是这么销毁的。
这就是整个函数栈帧创建和销毁的全部过程了。