(3)半动态变量( Semidynamic variable)如在单元U中,有 ALGOL68的说明: l: m int a; l: n real b 它定义了体积为m的整型数组a和体积为n的实型数组b。如 果m和n都不是常量,它们的值只有当U被激活时才能确定, 那么编译时不可能为它们作存储分配。即使在活动记录中 为它们的存储作安排也不可能。虽然在本例中,数组a相对 于活动记录的位移有可能在编译时确定,但它的长度在编 译时是不能确定的,因此紧接着要作存储分配的数组b在活 动记录中的相对位移,在编译时也不能确定了。因此,动 态数组不可能绑定于活动记录的常量位移。像动态数组这 类变量,称为半动态变量
11 (3) 半动态变量(Semidynamic Variable) 如在单元U中,有 ALGOL 68的说明: [l:m] int a; [l:n] real b 它定义了体积为m的整型数组a和体积为n的实型数组b。如 果m和n都不是常量,它们的值只有当U被激活时才能确定, 那么编译时不可能为它们作存储分配。即使在活动记录中 为它们的存储作安排也不可能。虽然在本例中,数组a相对 于活动记录的位移有可能在编译时确定,但它的长度在编 译时是不能确定的,因此紧接着要作存储分配的数组b在活 动记录中的相对位移,在编译时也不能确定了。因此,动 态数组不可能绑定于活动记录的常量位移。像动态数组这 一类变量,称为半动态变量
对于半动态变量,在编译时可以在活动记录中为它们安排 描述符。描述符是数据对象的属性集,记录着程序实体的 约束信息。对上例中的动态数组a和b来说,它们的描述符 包含两个元素:一个元素是指向a或b在活动记录中的首址 的指针,另一个元素则记录了动态数组每一维的上下界。 很明显,数组的维数是静态可知的,因此描述符的长度也 是静态可知的,所以它们在活动记录中的位移也是可确定 的。只是描述符的内容,即数组在活动记录中的首址和每 维的上、下界,在编译时是不可知的,需待U被激活并运 行至动态数组说明时,方可确定每一维的上、下界,将它 们记入描述符的第二个元素中,并据此算出数组的体积D, 在刚建立起来的U的活动记录顶端,为数组分配一个大小为 D的存储区,将数组描述符的第一个元素指向该存储区。以 后对该数组的任一元素的访问,可通过描述符计算出该数 组元素对数组起始元素的位移而获得访问地址。 12
12 对于半动态变量,在编译时可以在活动记录中为它们安排 描述符。描述符是数据对象的属性集,记录着程序实体的 约束信息。对上例中的动态数组a和b来说,它们的描述符 包含两个元素:一个元素是指向a或b在活动记录中的首址 的指针,另一个元素则记录了动态数组每一维的上下界。 很明显,数组的维数是静态可知的,因此描述符的长度也 是静态可知的,所以它们在活动记录中的位移也是可确定 的。只是描述符的内容,即数组在活动记录中的首址和每 一维的上、下界,在编译时是不可知的,需待U被激活并运 行至动态数组说明时,方可确定每一维的上、下界,将它 们记入描述符的第二个元素中,并据此算出数组的体积D, 在刚建立起来的U的活动记录顶端,为数组分配一个大小为 D的存储区,将数组描述符的第一个元素指向该存储区。以 后对该数组的任一元素的访问,可通过描述符计算出该数 组元素对数组起始元素的位移而获得访问地址
(4)动态变量 Dynamic variable)有一类数据对象,即使到 它所在单元被激活时,其活动记录的长度仍不能确定,因 为这类数据对象的存储区域的大小在运行时仍有可能发生 变化。例如APL的变量可以是动态类型的,它不是显式说 明,而是由运行时当前值隐式确定,并随运行路径的变化 而变化着。又例如 ALGOL68的灵活数组( Flexible array), 它的界是可变的,在运行中可能是二维的,也可能是三维 的。再如PL和 Pascal等语言中,允许数据的动态扩大和压 缩,由一组结点构成的数据结构,结点可以动态加入,也 可以动态删除。像链表和树就是这样的数据结构,结点由 指针来连接,结点在程序运行时分配。对于这些在运行时 可能改变变量类型和变量结构的,不仅数据对象本身不能 静态分配,而且它们的描述符的内容,甚至描述符的长度 都不是静态可确定的,这类变量称为动态变量。总之,动 态变量表示的数据对象的长度和个数,以及它们的描述符 的内容和长度,都可能在它的生存期中改变。 13
13 (4) 动态变量(Dynamic Variable) 有一类数据对象,即使到 它所在单元被激活时,其活动记录的长度仍不能确定,因 为这类数据对象的存储区域的大小在运行时仍有可能发生 变化。例如APL的变量可以是动态类型的,它不是显式说 明,而是由运行时当前值隐式确定,并随运行路径的变化 而变化着。又例如ALGOL 68的灵活数组(Flexible Array), 它的界是可变的,在运行中可能是二维的,也可能是三维 的。再如PL/1和Pascal等语言中,允许数据的动态扩大和压 缩,由一组结点构成的数据结构,结点可以动态加入,也 可以动态删除。像链表和树就是这样的数据结构,结点由 指针来连接,结点在程序运行时分配。对于这些在运行时 可能改变变量类型和变量结构的,不仅数据对象本身不能 静态分配,而且它们的描述符的内容,甚至描述符的长度 都不是静态可确定的,这类变量称为动态变量。总之,动 态变量表示的数据对象的长度和个数,以及它们的描述符 的内容和长度,都可能在它的生存期中改变
四、存储分配模式 根据以上讨论,存储分配可采取静态、栈式和堆三种方式。 1.静态分配 如果程序语言只允许静态变量,那么变量与存储区域的绑 定关系在编译时便可建立,并完成存储分配。这一类语言 便是静态语言。它们不允许递归调用,不允许动态数组, 不允许动态类型的数据对象,即不允许有非静态变量。 FORTRAN、 COBOL便是静态语言。 14
14 四、存储分配模式 根据以上讨论,存储分配可采取静态、栈式和堆三种方式。 1. 静态分配 如果程序语言只允许静态变量,那么变量与存储区域的绑 定关系在编译时便可建立,并完成存储分配。这一类语言 便是静态语言。它们不允许递归调用,不允许动态数组, 不允许动态类型的数据对象,即不允许有非静态变量。 FORTRAN、COBOL便是静态语言
2.栈式分配 凡不满足静态分配条件的语言,自然归入了动态分配类 但对半静态变量和半动态变量来说,所有局部变量在单元 激活时隐式建立,活动记录长度或者静态可确定,或者在 单元被激活时确定。同时,各单元之间的调用关系遵循 “后进先出”模式,即单元A被激活,建立起一个单元实例, 分配了A的活动记录。执行中A调用了单元B,于是A的活 动暂中断(A的活动记录并未撤消,仍保留着),为激活了的 B建立一个单元实例,分配B的活动记录。随着B运行结束, 完成了B的当前实例,释放B的活动记录,返回到被暂时中 断的单元A,继续A的活动,直至A的活动结束时,才撤消 A的活动记录。这表明单元B的生存期嵌套在A的生存期中, 即它们之间存在着动态嵌套的关系,相应的活动记录的建 立和撤消满足后进先出的模式。因此,我们选择栈来分配 活动记录是十分自然的。由于这个原因,半静态变量和半 动态变量也称为栈变量( Stack variable) 15
15 2. 栈式分配 凡不满足静态分配条件的语言,自然归入了动态分配类。 但对半静态变量和半动态变量来说,所有局部变量在单元 激活时隐式建立,活动记录长度或者静态可确定,或者在 单元被激活时确定。同时,各单元之间的调用关系遵循 “后进先出”模式,即单元A被激活,建立起一个单元实例, 分配了A的活动记录。执行中A调用了单元B,于是A的活 动暂中断(A的活动记录并未撤消,仍保留着),为激活了的 B建立一个单元实例,分配B的活动记录。随着B运行结束, 完成了B的当前实例,释放B的活动记录,返回到被暂时中 断的单元A,继续A的活动,直至A的活动结束时,才撤消 A的活动记录。这表明单元B的生存期嵌套在A的生存期中, 即它们之间存在着动态嵌套的关系,相应的活动记录的建 立和撤消满足后进先出的模式。因此,我们选择栈来分配 活动记录是十分自然的。由于这个原因,半静态变量和半 动态变量也称为栈变量(StackVariable)