函数调用时堆栈是如何分配的?使用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