一、 什么是栈帧?
首先引用百度百科的经典解释:“栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。

实际上,可以简单理解为:栈帧就是存储在用户栈上的(当然内核栈同样适用)每一次函数调用涉及的相关信息的记录单元。也许这样感觉更复杂了,好吧,让我们从栈开始来理解什么是栈帧...

栈帧表示程序的函数调用记录,而栈帧又是记录在栈上面,很明显栈上保持了N个栈帧的实体,那就可以说栈帧将栈分割成了N个记录块,但是这些记录块大小不是固定的,因为栈帧不仅保存诸如:函数入参、出参、返回地址和上一个栈帧的栈底指针等信息,还保存了函数内部的自动变量(甚至可以是动态分配内存,alloca函数就可以实现,但在某些系统中不行),因此,不是所有的栈帧的大小都相同。

二、实例

#include <stdio.h>

int Add(int a,int b)
{	
	int c = 0;
	c = a+b;
	return c;
}
int main()
{
	int a = 3;
	int b = 4;
	int sum = 0;
	sum = Add(a,b);
	printf("%d\n",sum);
	return 0;
}

以上是一个求和函数的例子,

接下来先了解一下栈帧的基本结构



当调试程序的时候,查看【调用堆栈】,如下图


我们发现,在main函数执行之前,tmainCRTStartup函数先调用,然后_tmainCRTStartup函数,然后才执行的main函数。

每次执行程序都会进行函数调用,我们把这个过程称为函数调用过程,这个过程主要为函数开辟栈空间,用于保存本函数调用时临时变量的保存,这块空间我们称为函数栈帧。

那么问题来了,我们如何维护栈帧呢?

接着,我们需要了解一下寄存器,在函数调用过程中ebp和esp两个寄存器存放了维护这个栈的栈顶和栈低的指针。

比如:分配栈空间时,ebp存放指向函数栈帧栈低的指针,esp存放了指向函数栈帧栈顶的指针。


当我们详细研究函数调用过程,我们还需要知道一点汇编代码。

1、从main函数的地方开始,对main函数调用就需要main函数创建栈帧,接下来,我们看看main函数的栈帧是如何创建的。



2、接下来就是sum函数调用过程


按F11我们执行call指令,再按F11我们跳转到sum函数执行代码处


最后,就是函数的返回部分



有人问了,我们为啥要学习栈帧呢?

我们看个案例

#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;
}

不同的环境下结果也是不同,VC6.0下运行结果是

然而,在VS中是可以编译,但是运行程序会报错,直接崩溃。

在centos6.5上编译不过去的,这就是