函数栈帧的创建和销毁(图+解析)

注:不同的编译器函数栈帧的创建是有差异的,本篇文章采用的环境是VS2013。

Main函数也是被调用的

补充:push 给栈顶放一个元素,也就是压栈

           pop  从栈顶删除一个元素,也就是出栈

           move exc  39b  把39b里的值放入exc寄存器中(exc、39b均是例子)

           add  加

           sub  减

           lea  edi  [xxx]   加载有效地址,把xxx加载edi里(edi、xxx均是例子)

这些属于汇编内容,理解即可。

1.寄存器

寄存器说白了就是一个临时储存东西的地方,随用随取,像你衣服上的口袋一样。

本次可能要用到的寄存器有:ebp,esp,eax,ebx,ecx,edx等等

ebp(栈顶指针)和esp(栈底指针)存储的是地址,这两个地址是用来维护函数栈帧的。

2.函数栈区的创建

每一个函数调用都要在栈区开辟一块空间。

以main函数为例

一开始mainCRTStartup调用_tmainCRTStartup,随后_tmainCRTStartup调用main

栈是从下往上放的,一开始放入_tmainCRTStartup,让寄存器ebp存储栈底的地址,让esp存储栈

顶的地址,两个地址是用来维护函数栈帧的。

函数栈帧的创建和销毁(图+解析)_第1张图片

随后把ebp的值push到栈顶,esp顺势往上挪,让ebp的值等于esp存储的值(也就是说此时两个寄存器指向同一个地方)。

函数栈帧的创建和销毁(图+解析)_第2张图片

esp的地址减小,esp往上挪,这样就出现了给main使用的空间。

函数栈帧的创建和销毁(图+解析)_第3张图片

下面的“组合技”是给函数栈区上的空间初始化:

函数栈帧的创建和销毁(图+解析)_第4张图片

给局部变量赋值的方法:采用move在已开辟的栈区内存放

以上是main函数栈区创建的过程,我们自行定义的函数栈区创建也是如此。

3.定义的函数的传参

要传入函数的变量会存放到寄存器里存好,然后找到地址再进行调用。

形参只是实参的备份,只是把实参的内容复制到寄存器里,形参从寄存器里取出变量,因此形参的

变化不会影响实参。

push参数是从右到左。如max(a,b) 是先push_b再push_a的。

函数栈帧的创建和销毁(图+解析)_第5张图片

4.函数的返回值

将要返回的值放入寄存其中

5.函数栈帧的销毁

首先pop栈顶的寄存器值,然后把ebp的值赋给esp,也就是让esp与ebp指向同一个地方,这样直接把先前开辟的栈区释放了。

我们调用完函数后将栈顶的(ebp-main)弹到ebp中,让ebp重新回到main的栈底,栈顶删除后esp顺势下移。再次弹出栈顶(此时栈顶存放的是call指令下一条的地址),函数执行ret指令,返回原函数。然后销毁存形参值的寄存器。

mian函数的销毁与定义的函数销毁类似。

———————————————————————————————————————————

以上就是本文的内容,谢谢!

你可能感兴趣的:(C语言,c语言)