C语言复习笔记--函数栈帧创建与销毁

        在上一篇博客中说道了递归过深会导致栈溢出,而这与函数栈帧有关这篇中就说说函数栈帧.

        在这里使用环境是vs2013,不要用太高级编译器,高级编译器不好观察函数栈帧创建和销毁过程.且在不同的编译器下栈帧创建是有差异的,具体的要看编译器实现.

        说道函数栈帧,就要谈到寄存器,今天主要要用ebp,esp两个寄存器.这两个寄存器中存放的是地址,这两个地址是用来维护函数栈帧的.我们也知道每一个函数的函数调用都会在栈上开辟一块空间.(栈使用地址是由高到低的)

        如上图所示上图的代码这个程序main函数就会在栈上开辟一块空间,ebp为栈底指针,esp为栈顶指针,去维护当前在使用的函数栈帧.

        注:在VS2013中可以看到main函数也是被其他函数调用的.

        在这部分要用到汇编代码去理解函数栈帧的创建和销毁.

        首先,因为我们已知main函数是其他函数调用的,所以在调用main之前栈上就应该有一段栈帧.如下图_tmainCRTStartup所示.

        下面让我们来看下main函数的汇编,第一行,push是压栈在栈上压入ebp的值,同时esp作为栈顶指针要一直指向栈顶,在每次压栈后都会向上移动.最后栈的结果就大概如下图所示.

        第二行,mov指令是让ebp移动到esp的位置上.在这行指令后ebp,esp暂时都指向栈顶.如下图

        第三行是让esp减去16进制下的0E4这个数字,因为栈是从高地址向低地址开空间,所以这行实际上是为main函数开辟了空间.如下所示:

        下三行继续是压栈.前面已经说过类似的操作了,经过下面三次压栈,栈区大概会变成下图所示的样子.

        下面那行后面开起来很乱其实是下面第一行这样.lea指令是计算某部分地址并将地址存到寄存器中.所以下面指令就是计算ebp到ebp-0E4h的地址并存到edi中.结合他下面的三行指令,他们一起将新开辟的(上面空白的)空间给上随机的初始值0CCCCCCCh.(这就是如果不进行初始化然后直接打印,会打印出超级的大数的原因,这里的初始随机值是编译器随即设定的,编译器之间可能会有不同)执行之后的结果大概如下图.

         再下面的三行,就是在已经开好的函数栈帧中给inta,b,c分配存储他们的空间.并且按照初始化的值给对应的空间初始化.大概如下所示(以及这里这三个int是不是挨着的完全取决于编译器)

        接下来我们继续看下面的汇编指令.

        第一行是指将ebp-14h这个位置上的值放入eax寄存器中,根据上面已经讲解过的指令可以得知ebp-14h这个位置就是存储b的位置,紧接着的指令是将eax压栈.所以这两句指令是指将b的值拷贝一份后压入栈顶,这两行的下两行与这两行相似,是将a的值拷贝一份后压入栈顶.进行之后的栈类似于下面图片.(这个大步骤就是函数调用时的传参,由这个进行的步骤也可以知道函数传参是从右向左传参的)

        下面就到了call指令,call指令是调用函数的指令.call指令执行之后会跳转到call后面的地址所示的指令,并且将call后面紧跟着的指令地址压栈(方便表用后的返回).

        在call之后就进入到了Add函数.

        既然到了一个新函数中,那就要建立这个函数的函数栈帧,上面的大端和main函数建立栈帧基本一致,同样是开辟函数栈帧并且将空间内的内容全部用随机值处理.处理后大概如下图(图中空白部分应为0CCCCCCCh.

         下面就是给z分配空间并且给空间初始化为0.

         在z=x+y处,使ebp+8的值放到eax中,这里会发先ebp+8位置上放的正是实参a传参时的拷贝.下一行是将ebp+12位置上的值加到eax中,ebp+12位置上放的正是实参b传参时的拷贝.下面再将eax中算好的加法结果放到z的位置上,就此完成z=x+y.如下图:

        下面我们来看最后函数的返回:

        第一行把z中的结果放入寄存器中(因为z为局部参数,出了Add函数的作用域就会销毁,所以要借助寄存器返回).

        下面几句pop就是把之前压入栈顶的元素弹出,同时esp栈顶指针也会向下移.

        mov指令将Add函数栈帧空间释放使得ebp指向esp的地方.

        pop ebp中使ebp返回原来压入的main函数的ebp的位置.

        ret使程序回到main函数栈帧中,且下一条语句刚好是main函数中之前存好的call Add函数的紧挨着的下一条指令.        这样就完成了函数栈帧的释放.之后就会再执行main函数后面的指令了.

        以上就是我了解的函数栈帧的创建与销毁.我们下篇博客见.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值