一、栈帧的概念
什么是栈帧,度娘给出了如下的解答:
“栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构。”
我们在调用函数时,总是要和这位叫做栈帧的朋友打交道,那么,你有没有试图去了解过他呢?
二、栈帧的结构
在博主的另外一篇博客里曾经提到过进程的地址空间,我们现在再来看一下什么是程序的地址空间
在上图里,我们发现了一个我们很熟悉的数据结构叫做栈。(栈向下生长)
栈帧,用我们容易理解的话来说,是机器用栈来传递过程参数,存储返回信息,保存寄存器用于以后恢复,以及本地存储的一种结构。
为单个函数调用分配扥那部分栈我们称之为栈帧
让我们具体来看看一个运行时堆栈的结构吧
我们知道,栈的生长是从高地址到低地址的,每次在我们调用一个函数时实际上发生了如下的过程:
1. 将返回地址压栈(返回之后要执行的下一条指令地址),将调用者的ebp(基址)压栈
2. 让ebp指向现在esp的位置
3. 开辟新的空间以供被调用的函数使用
函数在调用时一定会保存其返回地址,这样在该函数结束时我们才能正确的找到我们需要执行的下一条语句以保证正确的程序执行流。
寄存器ebp为帧指针,而寄存器esp为栈指针,当程序运行时,栈指针可以移动,而大多数信息的访问时通过帧指针获取
栈帧的起始位置应该是从保存帧指针的位置开始计算,栈帧的末尾位置则是保存返回地址处。
***我们所传的函数的参数实际上保存在调用该函数的函数的栈帧中。例如Q调用了P那么P的参数实际保存在Q的栈帧中。
既然我们知道了栈帧的结构 ,我们就可以利用栈帧的结构特点来做一些有趣的事,就比如,我们可以不使用变量名而改变变量的值
程序如图:
该程序的结果如下图
我们发现莫名其妙地a的值就被默默地修改了,我们首先来看一下具体的程序的意思,我们在main函数中定义了两个变量,a,b,他们都在main函数的栈帧中,此时我用一个指针指向b的起始地址向高地址偏移了一整个数组的位置(数组的压栈顺序是从高地址到低地址处下标也是从高到低的),然后将其作为int*来解析的,这样我们就可以成功的找到我们的变量a并修改它的值。
实际上这是一种十分不安全的做法,并不推荐因为觉得有趣而常常这样写程序。