局部变量
众所周知,每个编程语言都存在局部变量,通常在定义或引用函数的时候使用,所以在编写程序时通常会使用很多的局部变量。不知道大家有没有想过,在程序进行汇编过程中,局部变量是存放在哪里呢?
要想知道在汇编过程中局部变量存在哪里,首先要了解汇编中存在什么,汇编中只存在有寄存器、栈、可写区段、堆和函数。那这里就会有人问了,局部变量不是在函数中引用的吗,那不应该是存放在函数的位置吗?答案不是的。
局部变量其实是存放在栈上的。为什么会存放在栈上呢?首先我们先来了解一下局部变量的一种特性“易失性”,那什么是易失性?这时候大家应该去想一下,函数通常是有返回值的对不对,那函数返回后,局部变量还存在吗?不存在。随着每次函数的返回局部变量都会失效,这就是局部变量的“易失性”,例如下图代码:
这里我用python3为例,定义了一个函数a(),在函数a()中有一个局部变量x1,它的值为1,然后将x1的值返回。相信这个函数大家都懂,很简单对不对,但是在下面我连续输出了两次函数a(),每次的值都是1,相信大家都觉得这很正常对不对,没有什么不一样的,这里我想说的就是这一点。函数中的局部变量在函数返回后,局部变量就会失效,当重新调用函数时又会重新生效,当返回之后又会失效,易失性就是如此。
栈帧
通过上面了解了局部变量以及局部变量的特性后,那接下来就让我们更深入一步探讨刚开始的问题:局部变量究竟在汇编中存放在哪里呢?
正因为局部变量存在有“易失性”,所以人们将局部变量存放在栈上,当每次在函数调用的时候,都会在栈上分配一部分空间来存储局部变量。每个函数在被调用的时候都会产生三种区域:存放局部变量的区域、存放返回地址的区域、存放参数的区域,如下图所示:
每个函数在被调用的时候都会产生这样一个区域,当调用的函数越多产生的区域也就越多,一层层的累计在栈上,这一片区域就叫做帧,因为是在栈上,所以也叫做栈帧。
帧指针
介绍完了栈镇,又迎来了一个问题:栈在内存中的区域不一定是固定的,而且每次调用函数的路径不同,栈帧的位置也不相同,那怎么才能正确的找到并引用局部变量呢?
答案是帧指针。虽然栈在内存中的位置会变化,但是局部变量在函数调用栈上的偏移是固定的,打个比方:假设程序其中一个函数在栈中内存的位置是a,栈帧的位置是b,栈帧相对于函数的偏移量是c,所以即使函数在栈中内存的位置变换为a1、a2、a3···,栈帧的位置变换为b1、b2、b3···,栈帧相对于函数的偏移量都是c。所以利用栈帧和函数的这种关系可以引用一个专门的寄存器来存放当前栈帧的位置,这就是帧指针。