第五章是到目前位置最让我头疼嘚一章不是因为难,是因为对最终的目的不太明确整章前半部分用十分精简的语言介绍了一个新的概念,活动记录也就是在函数调鼡时,一个调用栈究竟发生了什么事但是,在最终编码实现过程中总不能理解作者为什么给了这些变量?或者说完全不知道作者究竟想让我完成一个怎样的功能。纠结了好几天后来索性往后继续看,看看能不能找到其他的线索直到看完后一章,硬着头皮写了一半嘚时候才豁然开朗。原来作者是这个意图!所以如果你也不知道这章程序最终要得到一个什么样的结果,建议把后面一章也读了因為这两章讲的是同一个东西的不同部分。
先看看一些理论知识吧首先,这个活动记录就是我们通常意义上的函数调用堆栈,所以叒叫栈帧是内存中的一个区域,在这个区域中包含了一个函数中所有参数局部变量,临时变量返回值等信息,如果这种语言还允许嵌套的函数声明那么,还要有一个叫做静态链的东西这些东西在不同的目标机器上对应的布局是不一样的,书上介绍了一种标准布局
那么,一个函数怎么去访问他自己的变量呢用的是(栈指针+偏移量),因为对cpu而言加法运算是比较简单的,所以在实际中,栈指针是位于低地址表示栈的下界,而帧指针位于高地址表示栈的上界。这样就会发现其实,栈顶的地址要比栈底的地址低
其实并不是所有参数都要放入栈(内存)中的,有些变量可以放入寄存器中这样可以省去很多的访存时间。但是一个程序可以有多個函数,寄存器的个数却是有限那么,当一个函数调用另一个函数且都要使用同一个寄存器的时候,谁去将原来的寄存器中的内容的保存起来呢如果是调用其他函数的函数负责,则成为调用者保护如果是被调用函数负责,那么就是被调用者保护
通常情况下,我們会将传入一个函数的参数放入寄存器中但是,寄存器的数量是有限放不下的怎么办?例如对于f(n1 , n2 , ...,nx)的函数,我们会将前k个假如k為4,放入寄存器中剩下的x-k个我们就放入调用函数f的函数的栈帧的末尾,紧邻着栈指针这x-k个区域就称为传出参数。但是这x-k块区域對于被调用函数f来说就被称为传入参数思考一下,如果f又去调用了其他函数呢那么那k个参数就要被移出寄存器,放到f中称为局部变量嘚区域中这块区域是紧挨着传出参数(传入参数,对函数f而言)
那么,除了以上参数传递过程中有变量要被移出寄存器还有其怹一些情况需要将寄存器中的参数放到内存中。比如在一个函数定义中取了一个外部函数中的一个变量的地址(此时要求语言支持函数內部定义新的函数),那么这个变量就要写回内存要不然,你取到的地址是寄存器地址如果在以后的处理过程中这个变量被移出了寄存器,岂不是就找不到了么
当然,还有很多情况需要考虑这里就不一一列举了,虎书上已经讲的很明白了并且,对于所有本来茬寄存器中后来被移动内存中的变量称为逃逸。那么如何在一个函数内部取用外部函数的变量呢?这就要用一个称为静态来链的东西这个东西就是能让你在一个函数内部顺藤摸瓜的找到你需要的变量,类似于一个链表
好了,说了这么多我们来看看最后的实现吧。本章要实现的其实就是这个栈。在前几次的数据结构中保留了一个称为scape的区域没有用这是个bool变量,使用这个变量来标记tiger中一个变量是不是逃逸的如何计算一个变量是不是逃逸的呢?因为这些逃逸信息只有在完成这个程序扫描后才能知道所以,对得到的抽象语法樹进行两次扫描第一次得到变量的逃逸信息,第二次才是对对语法树进行类型检测同时生成栈帧。
我的代码并没有实现对抽象语法的逃逸变量检测我默认所有的变量都是可以逃逸的。所以本次代码的主要部分就是blogs.com/BlackWalnut/p/4559245.html