函数栈帧的创建和销毁

栈(stack) 是现代计算机程序里最为重要的概念之一, 几乎每一个程序都使用了 栈,没有栈就没有函数,没有局部变量,也就没有我们如今看到的所有的计算机语言。

只要理解了函数栈帧的创建和销毁,以下问题就能够很好的额理解了:

●局部变量是如何创建的?

●为什么局部变量不初始化内容是随机的?

●函数调用时参数时如何传递的?传参的顺序是怎样的?

●函数的形参和实参分别是怎样实例化的?

●函数的返回值是如何带回的?

在经典的计算机科学中,栈被定义为-种特殊的容器,用户可以将数据压入栈中(入栈, push) ,也以将已经压入栈中的数据弹出(出栈,pop) , 但是栈这个容器必须遵守一条规则: 先入栈的数据后出栈(First In LastOut,FIFO) 。就像叠成一叠的书籍, 先叠上去的书在最下面,因此要最后才能取出。

在计算机系统中,栈则是一个具有以 上属性的动态内存区域。程序可以将数据压入栈中,也可以将数据从栈顶弹出。压栈操作使得栈增大,而弹出操作使得栈减小。

在经典的操作系统中,栈总是向下增长(由高地址向低地址)的。

在我们常见的i386或者x86-64下,栈顶由成为esp的寄存器进行定位的。

1.再讲函数栈帧的创建前,我们先引入寄存器(register)的概念

寄存器中有 eax,ebx,ecx,edx ,ebp,esp这些常见的寄存器,而其中ebp,esp这两个寄存器中用于存放地址,这两个地址是用来维护函数栈帧的。

函数栈帧的创建和销毁_第1张图片相关的汇编指令:

函数栈帧的创建和销毁_第2张图片

 每一个函数的调用都会在栈区开辟一个空间,就是函数栈帧的空间,其中寄存器ebp,esp用于存放地址来维护函数栈帧的空间。

函数栈帧的创建和销毁_第3张图片

 下面来看具体例子

函数栈帧的创建和销毁_第4张图片

函数调用堆栈是反馈函数调用逻辑的,那我们可以清晰的观察到,main 函数调用之前,是由invoke_ main 函数来调用main函数.

 

函数栈帧的创建和销毁_第5张图片

那我们可以确定,invoke_ mai n函数应该会有自己的栈帧,main 函数和Add函数也会维护自己的栈帧,每个函数栈帧都有自己的ebp和esp来维护栈帧空间。

(在vs2013中调用堆栈,可以发现main函数的调用顺序为:mainCRTStartup --> _tmainCRTStartup --> main)

准备反汇编代码的环境,可以关闭下面选项,让汇编代码中排除一些编译器附加的代码。

函数栈帧的创建和销毁_第6张图片

 转到反汇编 ,VS编译器每次调试都会为程序重新分配内存,课件中的反汇编代码是一-次调试代码过程中数据,每次调试略有差异。

 函数栈帧的创建和销毁_第7张图片

接下来是反汇编代码:

函数栈帧的创建和销毁_第8张图片

函数栈帧的创建:

函数栈帧的创建和销毁_第9张图片

函数栈帧的创建和销毁_第10张图片

 函数栈帧的创建和销毁_第11张图片

 函数栈帧的创建和销毁_第12张图片

 对Add函数进行传参:

call命令:

函数栈帧的创建和销毁_第13张图片

函数栈帧的创建和销毁_第14张图片

Add函数的反汇编代码:

代码执行到Add函数的时候,就要开始创建Add函数的栈帧空间了。
将main函数的ebp压栈——> ebp的地址放入esp上——>计算新的esp——>将ebx,esi, edi 寄存器的值保存——> 形参访问:计算求和,在计算求和的时候,我们是通过ebp中的地址进行偏移访问到了函数调用前压栈进去的参数——>将求出的和放在eax寄存器尊准备带回
函数栈帧的创建和销毁_第15张图片

函数栈帧的创建和销毁_第16张图片

 图片中的a'和b'其实就是Add函数的形参x,y。这里的分析很好的说明了函数的传参过程,以及函数在进行值传递调用的时候,形参其实是实参的一份拷贝。对形参的修改不会影响实参。

 函数栈帧的销毁:函数栈帧的创建和销毁_第17张图片

 回到了call的下一条命令函数栈帧的创建和销毁_第18张图片

调用完Add函数,回到main函数:

函数栈帧的创建和销毁_第19张图片

 

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