运行时存储空间组织

学习途径:

  • 国防科技大学中国大学 MOOC

参数传递

参数传递主要有四种传递方式:

  • 传地址;
  • 得结果:传地址的变形。
    • 每个形参对应两个形式单元:第一个存放实参地址;第二个存放值。
    • 在过程体中的任何引用或赋值都看作对它的第二个单元的直接访问。
    • 过程完成返回之前,把第二个单元的值存放到第一的单元所指向中。
  • 传值;
  • 传名:不常见。
    • 在进入被调用过程之前不对实在参数预先进行计值,而是让过程提中每当使用到相应的形式参数时才祝词对它实行计值(或计算地址)。
    • 通常把实在参数处理成一个子程序(称为参数子程序),每当过程体中使用到相应的形式参数时就调用这个子程序。

运行时存储器的划分

一个目标程序运行时所需的存储空间包括:

  • 存储目标代码的空间 $$\Rightarrow$$ 算法

  • 存放数据项目的空间 $$\Rightarrow$$ 数据结构

  • 存放程序运行的控制或连接数据所需要的单元

编译程序组织存储空间须考虑的问题:

  • 过程是否允许递归?
  • 当控制从一个过程的活动返回时,对局部名称的值如何处理?
  • 过程是否允许引用非局部名称?
  • 过程调用时如何传递参数?“过程”是否可以做为参数被传递和作为结果返回?
  • 存储空间可否在程序控制下进行动态分配?
  • 存储空间是否必须显式地释放?

存储分配策略

主要有两种分配策略:

  • 静态分配策略:在编译时能确定数据空间的大小,并且为每个数据项目确定出在运行时刻的存储空间中的位置。比如:FORTRAN 语言。

  • 动态分配策略:在编译时不能确定运行时数据空间的大小,允许递归过程和动态申请释放内存。

    • 栈式动态分配、堆式动态分配。

    • 函数调用使用栈式分配策略,变量空间使用堆式动态分配。

非嵌套过程语言

特点:

  • 允许过程递归调用、可以允许可变数组
  • 过程定义不允许嵌套(即 不可以在函数内部定义函数)
  • 比如:C 语言。

其过程管理通过栈式管理,一个程序中,按以下方式调用 main -> Q -> R -> Q,则其运行栈结构大致如下:

../non-nested-process.png

过程调用和过程返回:

  • 过程调用的语句 P(T1, T2, ..., Tn),翻译称下面的代码序列:

    1
    2
    3
    4
    5
    
    par T1
    par T2
    ...
    par Tn
    call P, n
    
  • 过程调用的目标代码通过以下的方式生成:

    每个 par Ti 可以翻译成如下的指令:

    1
    2
    3
    4
    5
    
    # 传值方式翻译
    (i+3)[TOP] := Ti
    
    # 传地址方式翻译
    (i+3)[TOP] := addr(Ti)
    

    对于 call P, n 则翻译成以下的指令

    1
    2
    3
    
    1[TOP] := SP
    3[TOP] := n
    JSR P			# 跳转指令
    

    转进过程 P 后,首先执行下述指令

    1
    2
    3
    
    SP := TOP + 1
    1[SP] := 返回地址
    TOP := TOP + L
    

    其中,L 为过程 P 的火丁记录所需要的单元数,在编译时可确定。

  • 过程返回时,应执行下面的指令:

    1
    2
    3
    4
    
    TOP := SP - 1
    SP := 0[SP]
    X := 2[TOP]
    UJ 0[X]
    

嵌套过程语言

特点:

  • 允许过程递归调用、也可以允许可变数组
  • 过程定义允许嵌套
  • 比如:PASCAL 语言

因为程序运行时,过程可以访问外层过程的变量。程序运行时,必须能知道它所有外层过程的当前活动记录的起始地址。

非局部名字的访问–静态链表方法

嵌套层次:

  • 主程序的层次为 0
  • 在第 i 层中定义的过程,其层次为 i+1

每一个局部变量用一个 嵌套层数 + 地址偏移量 的二元组唯一确定。并增加以下两个变量:

  • 动态链:指向本过程的调用过程的活动记录的起始地址,也称控制链。

  • 静态链:指向本过程的直接外层过程的活动记录起始地址,也称存取链。

比如,一个 $$P \overset{call}{\rightarrow} S \overset{call}{\rightarrow} Q \overset{call}{\rightarrow} R \overset{call}{\rightarrow} R$$ 的调用栈帧如下图所示:

../nested-language-static-chain.png

对于调用过程 $$P_1 \xrightarrow{call} P_2$$,如何根据调用过程 P1 的活动记录建立 P2 的静态链?

  1. 第 N 层过程调用第 N+1 层过程:P2 的静态链为调用过程 P1(第 N 层过程的)的最新活动记录的起始地址。
  2. 第 N 层过程调用第 N 层过程:P2 的静态链为调用过程 P1(第 N 层过程)的静态链的值。
  3. 第 N 层过程调用第 N-x 层过程:P2 的静态链为沿着调用过程 P1(第 N 层过程)的静态链向前走 x 步到达的活动记录的静态链的值。

非局部名字的访问–Display 表方法

Display 表:

  • 在活动记录中增加一个 Display 表:
    • 进入一个过程时,