函数调用时堆栈是如何分配的?使用VC2005在i386系列机器上进行试验,发现与编译选项有很大的关系。使用cl.exe不加任何选项直接编译,结果大致如下
高地址
高地址 | 传入参数 |
返回地址 | |
调用函数的EBP值 | |
局部变量 | |
局部变量数组 | |
。。。(标注1) | |
低地址 |
小结
1)传入参数的入栈顺序与调用约定有关,_cdecl和_stdcall这两种调用约定都是从右至左入栈。
2)如果函数中定义了局部变量数组,无论数组声明的位置如何,总是被分配到栈顶位置,即被分配在所有其它局部变量的下面(比其它局部变量地址低)。
3)如果有多个局部变量,那么它们被分配的地址顺序是不能确定的。
4)参考其它文章(标注1)位置还有调用函数的EBX、ESI、EDI值,但是按照我的实验环境,并没有发现这些值。
5)_cdecl由调用函数清除压栈参数(调整ESP),而_stdcall由被调用者清除。在下面这个例子中。main调用了_cdecl fun,由main负责清除传递给fun的参数5000,100(b, a). fun调用了_stdcall foo,由foo清理3,2(n, m)
测试代码如下:
int __stdcall foo(int m, int n)
{
int p=0x12;
int q=0x13;
printf("the address of m is %p-->m=%X\n", &m,m);
printf("the address of n is %p-->n=%X\n", &n,n);
printf("the address of p is %p-->p=%X\n", &p,p);
printf("the address of q is %p-->q=%X\n", &q,q);
for (int i=0; i<20; i++)
printf("the address of %p--->%X\n", (&q+i), *(&q+i));
return p+q;
}
void fun(int a, int b)
{
int x=2;
int z[2]={4,5};
int y=3;
printf("the address of a is %p-->a=%X\n", &a,a);
printf("the address of b is %p-->b=%X\n", &b,b);
printf("the address of x is %p-->x=%X\n", &x,x);
printf("the address of y is %p-->y=%X\n", &y,y);
printf("the address of z[0] is %p-->z[0]=%X\n", &z[0],z[0]);
printf("the address of z[1] is %p-->z[1]=%X\n", &z[1],z[1]);
foo(x, y);
}
int _tmain(int argc, _TCHAR* argv[])
{
int c=0x987;
int d=0x789;
fun(0x100, 0x5000);
return 0;
}
输出结果
the address of a is 0012FF60-->a=100
the address of b is 0012FF64-->b=5000
the address of x is 0012FF50-->x=2 //*** x声明在y之前,地址比y小(1)
the address of y is 0012FF54-->y=3
the address of z[0] is 0012FF48-->z[0]=4
the address of z[1] is 0012FF4C-->z[1]=5
the address of m is 0012FF40-->m=2
the address of n is 0012FF44-->n=3
the address of p is 0012FF34-->p=12 // ***p声明在q之前,地址比q大,结合(1)可说明小结3)
the address of q is 0012FF30-->q=13
the address of 0012FF30--->13 // foo参数
the address of 0012FF34--->12 //
the address of 0012FF38--->12FF58 // fun函数的EBP
the address of 0012FF3C--->40115D //返回地址
the address of 0012FF40--->2 //***
the address of 0012FF44--->3 //*** //fun函数调用_stdcall foo从右至左入栈(2)
the address of 0012FF48--->4 //*** fun函数局部数组地址总在栈顶,虽然它的声明在x和y之间。见小结2),紧接着的低地址为foo入栈参数,说明小结4)
the address of 0012FF4C--->5 //***
the address of 0012FF50--->2 // *** fun函数局部变量x地址
the address of 0012FF54--->3 // *** fun函数局部变量y地址
the address of 0012FF58--->12FF70 // main的EBP
the address of 0012FF5C--->401193 //fun返回地址
the address of 0012FF60--->100
the address of 0012FF64--->5000 //main调用_cdecl fun从右至左入栈,结合(2)可说明小结1)
the address of 0012FF68--->987
the address of 0012FF6C--->789
the address of 0012FF70--->12FFC0
the address of 0012FF74--->401425
the address of 0012FF78--->1
the address of 0012FF7C--->3835A8