【C语言】栈和栈帧,以及栈帧创建和销毁的过程

来源:互联网 发布:天刀捏脸数据金木研 编辑:程序博客网 时间:2024/05/22 10:48

        栈和栈帧,以及栈帧的创建和销毁的过程

      C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。


      栈帧表示程序的函数调用记录,而栈帧又是记录在栈上面,很明显栈上保持了N个栈帧的实体,那就可以说栈帧将栈分割成了N个记录块,但是这些记录块大小不是固定的,因为栈帧不仅保存诸如:函数入参、出参、返回地址和上一个栈帧的栈底指针等信息,还保存了函数内部的自动变量(甚至可以是动态分配内存,alloca函数就可以实现,但在某些系统中不行),因此,不是所有的栈帧的大小都相同。


注意:EBP指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;ESP所指的栈帧顶部和系统栈的顶部是同一个位置。

下面利用简单的add函数简单的向大家讲解栈帧的创建和销毁过程!

#include<stdio.h>#include<stdlib.h>int add(int x, int y){int ret = 0;ret = x + y;return ret;}int main(){int a = 10;int b = 20;int ret = add(a,b);printf("%d\n", ret);system("pause");return 0;}

在VS2015环境中:我们生成的汇编代码来逐步分析这个函数运行的过程;截取部分反汇编。main函数在mainCRTstartup中调用,先为mainCRTsturtup开辟栈帧,有栈底,有栈顶。现在开始压栈。如下图所示:


        对上面的关键词进行分析解释,寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)




      而图中的move ebp,esp 意思是把esp的内容给ebp,ebp存了esp里面的内容,所以现在也指向栈顶空间。同样,esp减去0E4h大小的值,因为栈空间是从高地址向低地址走的,所以这个步骤就是在ebp的上面开辟了0E4h这个大小的一个空间。


      同样,把 39h存到ecx里面,把0cccccccch存到eax这个寄存器中。rep 重复的意思,dword就是doubleword,push eax,然后进行压栈,esp指向存放寄存eax的空间地址。call指令,调用函数,在执行call指令同时,在栈顶ecx又开辟了一开新空间,用于存放call指令下一条指令的地址。



      上图中可以看出:从add()函数返回main()函数,这样call指令就完成了。程序走完自动会到当初call指令的下一条指令,即把形参也弹出去了,add函数栈帧的穿透就创建和销毁已经完成。

下面附上流程图:




总结:

  每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量,每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧维护着函数调用所需要的各种信息。函数的返回地址和参数,保存当前函数调用前的“断点”信息,也就是函数调用前的指令位置,以便在函数返回时能够恢复到函数被调用前的代码区中继续执行指令。函数栈帧的大小并不固定,一般与其对应函数的局部变量多少有关。函数运行过程中,其栈帧大小也是在不停变化的!


最后,新手上路,大神勿喷。欢迎大家进行批评改正!微笑



阅读全文
1 0