今天C ++课上看了一下栈的概念,觉得挺有意思的,所以打算写写自己对栈的理解。


一,栈的含义:

       栈 (Stack  在汉语中类似地称为堆叠,英文的计算机科学中一种特殊的串列形式的抽象艺术艺术数据类型其特殊之处在于只能网是什么意思?网求允许在链表数组的一端(称为堆栈顶端指针,)进行加入数据()和输出数据(弹出)的运算。栈另外也。用可以一维数组链表的形式来完成。堆栈的一个另外的相对操作方式称为队列

由于堆栈数据结构只允许在一端进行操作,因而按照后进先出(LIFO,Last In First Out)的原理运作。

不同处理器和编译器的堆栈布局,函数调用方法都可能不同,但堆栈的基本概念是一样的。


二,栈的存在原因

我们知道,C ++变量中有局部变量和全局变量之分。其中,局部变量只在调用它所在的函数时才会生效,一旦函数返回就失效了,所以很多局部变量的生存周期远远低于整个程序的运行周期。如果为每个局部变量分配不同的空间,也就它们分别像全局变量一样先用确定的地址定位,则由于失效,这分配的地址就没有用了,这将导致内存空间的利用率大大降低。

更重要的问题是,如果发生了递归调用,会存在某个函数尚未返回,而对它的另一次调用又发生的情况。

通过这种多次调用,相同名称的局部变量会有不同的值,这些值必须同时保存在内存之中,而且又不能互相影响,所以它们必须要储存在不同的地址,像全局变量一样分配确定的地址肯定是行不通的。   

对于函数形参,与局部变量也非常相似,它们都不能通过像全局变量一样用固定的地址加以定位,而是需要存储在一种特殊的结构中,那就是栈。这种存储函数形参和局部变量的栈,称之为运行栈。

显然,运行栈的一段英文区域的内存空间,与存储全局变量的空间无异,只是寻址的方式不同而已。

运行栈中的数据分为一个一个栈帧每一个栈帧对应一次函数调用。所以栈帧中包含这次函数调用中的形参值,局部变量值,一些控制信息,临时数据(例如复杂表达式计算的中间值,某些函数的返回值)。

每次发生函数调用时,都会有一个栈帧被压入运行栈中,而函数调用返回后,相应的栈帧会被弹出。

一个函数在执行的过程中,能够直接随机访问它所对应的栈帧中的数据,即处在运行栈最顶端的栈帧的数据(执行中的函数的栈帧,纵处在运行栈的最顶端)。

当一个函数调用其他函数时,要为它调用的函数设置实参,具体方式是在调用前把实参压入栈中,运行栈中的这一部分空间是主调函数和被调函数都可以直接访问的,也就是参数的形式结合就是通过这一公共空间来完成的。

虽然每一个函数在被调用时的形参和局部变量的地址都是不确定的,但是它们的地址相对于栈顶的地址却的英文确定的,这样就可以通过栈顶的地址,来间接定位函数形参和局部变量。


三,与栈及栈帧有关的寄存器

用来记录栈顶地址的寄存器,称为栈指针,用来记录栈帧地址的寄存器被,称为帧指针。

以IA-32为例,尤指寄存器存储栈指针,而EBP寄存器存储帧指针。

对下面寄存器稍作讲解:

 寄存器是处理器加工数据或运行程序的重要载体,用于存放程序执行中用到的数据和指令。因此函数调用栈的实现与处理器寄存器组密切相关
 英特尔32位体系结构(简称IA-32 )处理器包含8个四字节寄存器,如下图所示:      

栈


栈帧指针:

      函数调用经常是嵌套的,在同时刻,堆栈中会有多个函数的信息。每个未完成运行的函数占用一个独立的连续区域,称作栈帧(Stack Frame)

       栈帧是堆栈的逻辑片段,当调用函数时逻辑栈帧被压入堆栈,当函数返回时逻辑栈帧被从堆栈中弹出。栈帧存放着函数参数,局部变量和恢复前一栈帧所需要的数据等。编译器利用栈帧,使得函数参数和函数中局部变量的分配与释放对程序员透明。编译器将控制权移交函数本身之前,插入特定代码将函数参数压入栈帧中,并分配足够的内存空间用于存放函数中的局部变量。使用栈帧的一个好处是使得递归变为可能,因为对函数的每次递归调用,都会分配给该函数一个新的栈帧,这样就巧妙地隔离当前调用与上次调用。

       栈帧的边界由栈帧基地址指针EBP和堆栈指针ESP界定(指针存放在相应寄存器中).ebp指向当前栈帧底部(高地址),在当前栈帧内位置固定;尤指指向当前栈帧顶部(低地址),当程序执行时ESP会随着数据的入栈和出栈而移动。因此函数中对大部分数据的访问都基于EBP进行。

为更具描述性,以下称EBP为帧基指针,尤其为栈顶指针,并在引用汇编代码时分别记为%EBP和%ESP。

函数运行栈的典型内存布局如下图所示:

栈

 注意,栈帧是运行时概念,若程序不运行,就不存在栈和栈帧。但通过分析目标文件中建立函数栈帧的汇编代码(尤其是函数序和函数跋过程),即使函数没有运行,也能了解函数的栈帧结构。通过分析可确定分配在函数栈帧上的局部变量空间准确值,函数中是否使用帧基指针,以及识别函数栈帧中对变量的所有内存引用。