我们大家学习C语言肯定都知道函数,但大家可能都只会用函数,对它的了解只是上层的,并不知道它的调用过程,今天我们就一起来深入的研究一下函数的调用过程。
首先,我们要知道的是,在函数调用时,程序将使用一个运行时堆栈,它里边存局部变量和返回地址,运行时堆栈由ebp(存放维护这个栈的栈底指针)和esp(存放维护这个栈的栈顶指针)两个寄存器维护,在栈空间开辟。
先看一段简单的代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int ret = Add(a, b);
printf("ret = %d\n", ret);
return 0;
}
在按f10逐步运行时,我们可以看见这样一个现象:
我们可以发现,其实main函数在_tmainCRTStartup函数中调用的,而_tmainCRTStartup函数是在mainCRTStartup被调用的。
在调用main函数时,我们为main函数分配栈帧空间,栈帧维护如下:
当我们要详细研究函数调用过程,就必须得看汇编代码。
我们先来看看main函数栈帧的创建。
接下来我们来看看Add函数的调用。
执行call指令的时候按F11,来到了这里:
再按F11就进入Add函数的执行代码处:
剩下的是函数返回部分:
到这里,我们已经把一个函数完整的调用过程分析完了,可能还有好多人不理解我们为什么要研究函数的调用过程,栈帧的销毁和创建。那我们就看一个题目吧。
在VC6.0环境中,下面代码的运行结果是什么?
#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;
}
我们先来猜一下它的输出结果是多少呢?很多人可能会猜出答案是20,没错,就是20,那这到底是为什么呢?我就简单来分析分析它的函数调用过程吧,感兴趣的话可以根据上边我分析的那个函数定义过程,转到它的反汇编,自己仔细研究一下。、