编译原理(第5版)-课件(更新) 2025第8章 目标程序运行时的存储组织 .pptx
编译程序需进行目标程序运行环境的设计和数据空间的分配。
本章主要介绍:
静态存储分配策略
动态存储分配策略
;8.1概述;运行时的环境:是指目标计算机的寄存器和存储器的结构,以及用来管理存储器并保存指导执行过程所需要的信息。
如编译程序从操作系统中得到一块存储区,以使目标程序在其上运行,该存储区需容纳生成的目标代码和目标代码运行时的数据空间。
;数据空间:包括用户定义的各种类型的数据对象所需的存储空间、调用过程所需的连接单元和组织输入/输出所需的缓冲区及保留中间结果和传递参数所需的临时单元等。
运行时的存储空间通常被划分成:目标区、静态数据区、栈区和堆区,如下图所示。
;;程序设计语言关于名字的作用域和生成期的定义规则决定了分配目标程序数据空间的基本策略。
在大部分现有编译中目标程序数据空间的分配策略有:
;静态存储分配策略
编译时对源程序中各数据项分配固定的存储空间,运行时始终不变。
动态存储分配策略
运行阶段动态地为源程序中的数据项分配存储空间。;(1)栈式动态存储分配
用一个栈作为动态分配的存储空间。
运行时,每当调用一个子程序(过程),所需存储空间就动态地分配于栈顶,退出时释放所占用的空间。
(2)堆式动态存储分配
运行时把存储区组织成堆,以便用户动态地申请或释放存储空间。;8.2静态存储分配;8.2静态存储分配;8.2静态存储分配; FORTRAN77采用的是静态存储分配,它的程序是段结构的,整个程序由主程序段和若干个子程序段组成,各程序段中定义的名字一般彼此独立,它的每个数据名所需的存储空间大小都是常量,并且不允许递归调用。这样,整个程序所需数据空间的总量在编译时就能完全确定,从而每个数据名的地址就可静态进行分配。
;8.2静态存储分配;CNSUME的代码;动态存储分配策略
运行阶段动态地为源程序中的数据项分配存储空间。
(1)栈式动态存储分配
用一个栈作为动态分配的存储空间。
运行时,每当调用一个子程序(过程),所需存储空间就动态地分配于栈顶,退出时释放所占用的空间。;8.3动态存储分配;如果一个程序设计语言允许递归调用、可变数组或每次调用都要重新分配局部变量,那么,就需要采用动态存储分配策略。因为对于这种情况,编译程序无法知道它在运行时需要多大的存储空间,它所需要的存储空间的大小需待运行时动态地确定。;为讨论方便,引入术语—活动记录。过程的活动记录是一段连续的存储区,用以存放过程的一次执行所需要的信息。;临时变量;3.静态链
指向静态直接外层最新活动记录地址的指针,用来访问非局部数据。
;4.形式单元
存放相应的实在参数的地址或值。
5.局部变量
一个子程序(过程)的局部变量。
;6.临时变量
比如计算表达式过程中需存放中间结果用的临时值单元。
;SP总是指向现行过程活动记录的起始地址
TOP则始终指向已占用的栈顶单元。;简单栈式存储分配
对于没有分程序结构,过程定义不允许嵌套但允许过程递归调用的语言,我们可以采用一种简单的栈式存储分配策略。
C语言就是满足上述特点的一种语言。
;C语言其过程的活动记录一般采用如图所示结构。
图中使用两个指针(SP和TOP)指示栈最顶端的数据区,SP总是指向现行过程活动记录的起点;TOP则始终指向已占用的栈顶单元。;由图可知,过程的每一个局部变量或形参在活动记录中的相对地址是确定的,因此可以知道程序运行时,变量和形参在栈上的绝对地址是:
绝对地址=活动记录基地址(SP)+相对地址
;相对地址:相对于活动记录起点的地址,它在编译时可完全确定。
对任何局部变量x的引用可表示为变址访问x[sp],此处x为变量x的相对地址。x[sp]表示地址为x+sp即x所在地址)
;voidp(intm)
{ ……
if(m1)
{q(m-1);
x--;
p(m-1);
}
……
}
main()
{p(x);}
;main()
{p(x);}
;在主函数调用p,在p中调用函数q,函数q再调用函数p的情况
;voidp(intm)
{ ……
if(m1)
{q(m-1);
x--;
p(m-1);
}
……
}
main()
{p(x);}
;函数q执行完毕后,函数p对自己调用的情况
注意:在函数中定义的静态变量必须存放在全局/静态区中,不能在函数的活动记录中分配。;如果在语言中允许过程嵌套定义(如Pascal语言),因为没有提供非局部变量的引用,所以前面所说的两种存储分配策略都不能使用了。这时我们需要设计一种新的存储分配策略。;现在我们分析的对象是允许过程嵌套定义的语言,因此会常常用到过程定义的层数,我们