关于函数在调用过程中的压栈和出栈问题在学习的时候就感觉很经典,对程序的把握可以提升一个台阶。
一.首先让我们写出一个简单的函数。(我是在vc6.0中实现,并不表示vs编译器底下不可以实现)。
#include<stdio.h>
int add(num1,num2)
{
int ret = 0;
ret = num1+num2;
return ret;
}
int main()
{
int num1 = 1;
int num2 = 2;
int ret = add(num1,num2);
printf("%d ",ret);
return 0;
}
1).需要声明是add函数中可以直接写成"return num1+num2",我在写博客的时候是故意写成这样,以便于后面的分析。
二.接下来,我们首先明确几个知识点。
1).栈
首先必须明确一点也是非常重要的一点,栈是向下生长的,所谓向下生长是指从内存高地址->低地址的路径延伸,那么就很明显了,栈有栈底和栈顶,那么栈顶的地址要比栈底低。对x86体系的CPU而言,其中
---> 寄存器ebp(base pointer )可称为“帧指针”或“基址指针”,其实语意是相同的。
---> 寄存器esp(stack pointer)可称为“ 栈指针”。
要知道的是:
--->ebp 在未受改变之前始终指向栈帧的开始,也就是栈底,所以ebp的用途是在堆栈中寻址用的。
—>esp是会随着数据的入栈和出栈移动的,也就是说,esp始终指向栈顶。
2).
假设函数A调用函数B,我们称A函数为"调用者",B函数为“被调用者”则函数调用过程可以这么描述:
(1)先将调用者(A)的堆栈的基址(ebp)入栈,以保存之前任务的信息。
(2)然后将调用者(A)的栈顶指针(esp)的值赋给ebp,作为新的基址(即被调用者B的栈底)。
(3)然后在这个基址(被调用者B的栈底)上开辟(一般用sub指令)相应的空间用作被调用者B的栈空间。
(4)函数B返回后,从当前栈帧的ebp即恢复为调用者A的栈顶(esp),使栈顶恢复函数B被调用前的位置;然后调用者A再从恢复后的栈顶可弹出之前的ebp值(可以这么做是因为这个值在函数调用前一步被压入堆栈)。这样,ebp和esp就都恢复了调用函数B前的位置,也就是栈恢复函数B调用前的状态。
如下图所示
三.在明确了这些知识之后,让我们返回上面那个简单的函数。
1).首先来看看我画出的图。
上面的图片能够粗略的表现函数调用的过程。
2).
所产生的汇编代码:
上面两幅图片是mian函数的栈帧。
上面的图片是add函数的栈帧。
3).在liunx平台下的汇编代码
以上为本人在学习函数在内存中的操作的一些经验,当然,本人才疏学浅,上述肯定会有纰漏,若有读者发现,请联系我及时改正。谢谢!
本文出自 “做一个小小小司机” 博客,请务必保留此出处http://10799170.blog.51cto.com/10789170/1715186