您的位置:首页 > 其它

函数调用过程栈帧变化详解

2013-12-19 11:13 537 查看
首先应该明白,栈是从高地址向低地址延伸的。每个函数的每次调用,都有它自己独立的一个栈帧,这个栈帧中维持着所需要的各种信息。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(地址地)。下图为典型的存取器安排,观察栈在其中的位置



 

入栈操作:push eax; 等价于 esp=esp-4,eax->[esp];如下图



出栈操作:pop eax; 等价于 [esp]->eax,esp=esp+4;如下图



我们来看下面这个C程序在执行过程中,栈的变化情况

void func(int m, int n) {

    int a, b;

    a = m;

    b = n;

}

main() {

...

    func(m, n);

L:  下一条语句

...



 

在main调用func函数前,栈的情况,也就是说main的栈帧:



从低地址esp到高地址ebp的这块区域,就是当前main函数的栈帧。当main中调用func时,写成汇编大致是:

push m

push n; 两个参数压入栈

call func; 调用func,将返回地址填入栈,并跳转到func



当跳转到了func,来看看func的汇编大致的样子:

__func:

        push ebp; 这个很重要,因为现在到了一个新的函数,也就是说要有自己的栈帧了,那么,必须把上面的函数main的栈帧底部保存起                        ; 来,栈顶是不用保存的,因为上一个栈帧的顶部讲会是func的栈帧底部。(两栈帧相邻的)

        mov ebp, esp; 上一栈帧的顶部,就是这个栈帧的底部

        ;暂时先看现在的栈的情况



                 ;到这里,新的栈帧开始了

                 sub esp, 8   ;  int a, b 这里声明了两个int,所以esp减小8个字节来为a,b分配空间

                 mov dword ptr [esp+4], [ebp+12];   a=m

                 mov dword ptr [esp], [ebp+8]; b=n         

   这样,栈的情况变为:



                    ret 8     ;  返回,然后8是什么意思呢,就是参数占用的字节数,当返回后,esp-8,释放参数m,n的空间

 

由此可见,通过ebp,能够很容易定位到上面的参数。当从func函数返回时,首先esp移动到栈帧底部(即释放局部变量),然后把上一个函数的栈帧底部指针弹出到ebp,再弹出返回地址到cs:ip上,esp继续移动划过参数,这样,ebp,esp就回到了调用函数前的状态,即现在恢复了原来的main的栈帧。


vs2010 下局部变量地址分配

记得局部变量在栈中的地址是连续分配,今天在VS2010上发现局部变量在栈中的地址不是连续分配,查了一些资料,有的说是编译器的分配地址的算法不同而至。具体如下: 

C++代码  


#include <iostream>  

using namespace std;  

  

int main()  

{  

    int iv;  

    cout << "&iv " << &iv << endl;  

    cout << "iv " << iv << endl;  

    int iv2 = 1024;  

    cout << "&iv2 " << &iv2 << endl;  

    int iv3 = 999;  

    cout << "&iv3 " << &iv3 << endl;  

    int iv4 = 100,iv5 = 200,iv6 = 300;;  

    cout << "&iv4 " << &iv4 << endl;  

    cout << "&iv5 " << &iv5 << endl;  

    cout << "&iv6 " << &iv6 << endl;  

    int &reiv = iv2;  

    cout << "&reiv= " << reiv <<endl;  

    int &reiv2 = iv;  

    cout << "&reiv2= " << reiv2 <<endl;  

    int &reiv3 = iv;  

    cout << "&reiv3= " << reiv3 <<endl;  

    int *pi;  

    cout << "&pi= " << pi <<endl;  

    *pi = 5;  

    cout << "*pi" << *pi << endl;  

    pi = &iv3;  

    const double di = 2.0;  

    cout << "&di= " << &di <<endl;  

    const double maxWage = 10.0;  

    const double *pc = &maxWage;  

    cout << "pc= " << pc <<endl;  

    cout << "*pc= " << *pc <<endl;  

    return 0;  

}  

输出:&iv 003DFBCC 
     &iv1 003DFBC0  
     &iv2 003DFBB4 
     &iv3 003DFBA8 
     &iv4 003DFB9C 
他们之间的地址相差12bytes 其中4bytes用于存储数据,8bytes用于存储其它
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: