函数的调用过程,栈帧的创建和销毁

我们大家学习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,那这到底是为什么呢?我就简单来分析分析它的函数调用过程吧,感兴趣的话可以根据上边我分析的那个函数定义过程,转到它的反汇编,自己仔细研究一下。、

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huaijiu123/article/details/80040750