6.1 概述
6.1.1 运行时的存储组织及管理
目标程序运行时所需要存储空间的组织与管理以及源程序中变量存储空间的分配。
6.1.2 静态存储分配和动态存储分配
静态存储分配
在编译阶段由编译程序实现对存储空间的管理,和为源程序中的变量分配存储的方法。
条件:如果在编译时能够确定源程序中变量在运行时的数据空间大小,且运行时不改变,那么就可以采用静态存储分配方法。
动态存储分配
在目标程序运行阶段由目标程序实现对存储空间的组织与管理,和为源程序中的变量分配存储的方法。
特点
- 在目标程序运行时进行分配。
- 编译时要生成进行动态分配的目标指令。
6.2 静态存储分配
6.2.1 分配策略
由于每个变量所需空间的大小在编译时已知,因此可以用简单的方法给变量分配目标地址。
- 开辟一数据区。(首地址在加载时确定)
- 按编译顺序给每个模块分配存储。
- 在模块内部按顺序给模块的变量分配存储,一般用相对地址,所占数据区的大小由变量类型确定。
- 目标地址填入变量的符号表中。
这种分配策略要求语言不允许指针或动态分配,不允许递归调用过程。典型的例子是Fortran77。
例子
6.2.2 模块(FORTRAN子程序)的完整数据区
编译程序除了要给源程序(模块)中的各种变量分配数据区以外,对子程序来说还要在数据区中保留返回地址,对形式参数也要分配存储以存放相应的实参的信息。另外在编译时还要分配临时变量空间(如存放表达式计算的中间结果等)。
6.3 动态存储分配
-
动态存储分配:由于编译时还不能具体确定某些数据空间的大小,故对它们分配存储空间必须在程序运行时进行。这时,编译程序生成有关存储分配的目标代码,实际上的分配要在目标程序运行时进行。这种分配方式称为动态存储分配。
-
对于分程序结构,而且允许递归调用的语言,常使用栈式动态存储分配,即使用一个类似于堆栈的“运行栈”来实现数据区的分配。
-
分配策略
整个数据区为一个堆栈- 当进入一个过程时,在栈顶为其分配一个数据区。
- 当退出一个过程时,撤消该过程的数据区。
例子
6.3.1 活动记录
一个典型的活动记录可以分为三部分:
- 局部数据区:存放模块中定义的各个局部变量。
- 参数区: 存放隐式参数和显式参数。
- 形参数据区:每一形参都要分配数据空间,形参单元中存放实参值或者实参地址。
- prev abp:存放调用模块记录基地址。函数执行完时,释放其数据区,数据区指针指向调用前的位置。
- ret addr:返回地址,即调用语句的下一条执行指令地址。
- ret value:函数返回值(无值则空)。
- display区:各外层模块活动记录的基地址。
对于例 1 中所举的程序段,模块 4 可以引用模块 1 和模块 3 中所定义的变量,故在模块 4 的display 区,应包括 AR 1 和 AR 3 的基地址。
变量二元地址(BL、ON)
- BL:变量声明所在的层次。可以用它找到该层数据区开始地址。(注意为嵌套层次,并列过程具有相同层次)
- ON:相当于显示参数区开始位置的位移(相对地址)。
例子:画运行栈
6.3.2 建造display区的规则
i 层模块调用 j 层模块
- 若 j=i+1,j层display区 = i层display区 + i层
- 若 j≤i 即调用外层模块或同层模块
6.3.3 运行时的地址计算
在LEV层模块中引用BL层模块定义的变量,该如何计算变量的地址?
- BL = LEV
addr := abp + (BL - 1) + nip + ONabp当前活动记录基指针值BL变量声明所在的层次。可以用它找到该层数据区开始地址。(注意为嵌套层次,并列过程具有相同层次)BL-1display区大小nip隐式参数区大小ON相当于显示参数区开始位置的位移(相对地址)
- BL < LEV
addr := display[BL] + (BL - 1) + nip + ONdisplay[BL]当前活动记录display区中第BL个元素
- else
write(“地址错误,不合法的模块层次”)
















