您的位置:首页 > 编程语言 > C语言/C++

C函数的调用过程   栈帧

2016-01-17 10:15 417 查看
C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量。首先,栈是从高地址向低地址延伸的。寄存器ebp指向当前的栈帧的底部(高地址),寄存器esp指向当前的栈帧的顶部(低地址)。先来看一个代码
#include <stdio.h>
void fun()
{
int tmp = 10;
int*p = (int*)(*(&tmp + 1));
*(p - 1) = 20;
}
int main()
{
int a = 0;
fun();
printf("a = %d\n", a);
return 0;
}
这个代码输出的a的值时多少呢? 答案是20。


调用fun()函数后居然把a的值改掉了,从上面的代码可以看到并没有给fun()函数传a的地址,那么fun()函数为什么会把a的值改掉呢? 回答这个问题之前我们需要了解C语言函数的调用过程。

为了更好的观察到上述函数的调用过程,我们在VC 6.0环境下查看一下它的汇编代码。

在这之前首先要知道: 寄存器ebp称为“基址指针”,在未受改变之前始终指向栈底,用途是:在堆栈中寻址。
寄存器esp称为“栈指针”,会随着数据的入栈出栈移动,也就是说始终指向栈 顶。



函数调用过程如下图所示: 图中有部分esp移动的过程没有画出,但最终不管如何入栈出栈esp会在销毁空间后指向创建空间前esp最后指向的位置

从上面的汇编代码,我们可以看到fun()函数是通过找到变量a所在的栈的栈底地址,进而找到a的地址,将a的内容改掉,这就解释了为什么没有给fun()传a的地址,却可以改变a的值。

总结:
函数在调用另一个函数之前会保存两个信息(1)函数调用完返回之后下一条指令的地址
(2)该函数的栈底的地址





最后分享一个使用ebp修改程序执行顺序的代码,并且最后esp回到原处,貌似什么都没发生的样子。

但是却在屏幕上多输出了一个funtest
#include <stdio.h>
void*p =NULL;
void*q =NULL;
void funtest()
{
int tmp =0;
int tmp2 =1;
printf("funtest\n");
*(int*)(&tmp+2) =p;
}
void swap(int *pa, int *pb)
{
int tmp=0;
p= *(&tmp+2);
q= &tmp+2;
*(&tmp+2) = &funtest;
tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a =10;
int b =20;
swap(&a,&b);
printf("main\n");
_asm{
sub esp,4
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息