第六章 运行时储存空间的组织和管理
过程和函数这样的程序单元统称为过程,程序运行时过程的一次执行称为过程的一次活动。过程的每次活动都需要可执行代码和存放所需数据的储存空间,所需的局部数据通常用一块连续的储存区来存放,称之为活动记录。过程p一次活动的生存期是从过程体开始执行到执行结束的时间,包括消耗在执行被p调用的过程所需的时间,以及再由这样的过程调用过程所花的时间等。
- 过程的活动
过程的一次执行称为过程的一次活动 - 活动记录
过程的活动需要可执行代码和存放所需信息的存储空间,后者称为活动记录 - 影响存储分配策略的语言特征
- 一个声明起作用的程序部分称为该声明的作用域
- 即使一个名字在程序中只声明一次,该名字在程序运行时也可能表示不同的数据对象
6.1.3 活动记录
6.1.4 局部数据的安排
- 字节是可编址内存的最小单位
- 变量所需的存储空间可以根据其类型而静态确定
- 一个过程所声明的局部变量,按这些变量声明时出现的次序,在局部数据域中依次分配空间
- 局部数据的地址可以用相对于某个位置的地址来表示
- 数据对象的存储安排还有一个对齐问题
6.1.5 程序块
- 本身含有局部变量声明的语句
- 可以嵌套
- 最接近的嵌套作用域规则
- 并列程序块不会同时活跃
- 并列程序块的变量可以重叠分配
6.2 全局存储分配策略
- 介绍程序运行时所需的各个活动记录在存储空间的分配策略
- 描述过程的目标代码怎样访问绑定到局部名字的存储单元
- 介绍三种分配策略
- 名字在程序被编译时绑定到存储单元,不需要运行时的任何支持
- 绑定的生存期是程序的整个运行期间
- 控制再次进入该过程时,局部变量的值和控制上一次离开时的一样
- 每个活动记录的大小是固定的
- 过程调用时保存信息的地址在编译时也是已知的
- 静态分配给语言带来限制
- 活动树的特点
- 每个结点代表某过程的一个活动
- 根结点代表主程序的活动
- 结点a是结点b的父结点,当且仅当控制流从a的活动进入b的活动
- 结点a处于结点b的左边,当且仅当a的生存期先于b的生存期
- 运行栈:把控制栈中的信息拓广到包括过程活动所需的所有局部信息(即活动记录)
- 过程调用和过程返回都需要执行一些代码来管理活动记录栈,保存或恢复机器状态等
- 过程调用序列
过程调用时执行的分配活动记录,把信息填入它的域中的代码 - 过程返回序列
过程返回时执行的恢复机器状态,释放活动记录,使调用过程能够继续执行的代码 - 调用序列和返回序列常常都分成两部分,分处于调用过程和被调用过程中
- 即使是同一种语言,过程调用序列、返回序列和活动记录中各域的排放次序,也会因实现而异
- 设计这些序列和活动记录的一些原则是
- 以活动记录中间的某个位置作为基地址
- 长度能较早确定的域放在活动记录的中间
- 一般把临时数据域放在局部数据域的后面
- 把参数域和可能有的返回值域放在紧靠调用者活动记录的地方
- 用同样的代码来执行各个活动的保存和恢复
- 活动记录的长度在编译时不能确定的情况
- 过程体中的非局部引用可以直接使用静态确定的地址
- 局部变量在栈顶的活动记录中,可以通过base_sp指针来访问
- 无须深入栈中取数据,无须访问链
- 过程可以作为参数来传递,也可以作为结果来返回
6.3.2 有过程嵌套的静态作用域
6.3.3 动态作用域
- 被调用过程的非局部名字a和它在调用过程中引用的是同样的存储单元
- 新的绑定仅为被调用过程的局部名字建立,这些名字在被调用过程的活动记录中占用的存储单元
- 实现动态作用域的方法
- 实参的左值传给被调用过程
- 引用调用可以如下实现:
- 值调用和引用调用的混合
- 复写-恢复调用可以如下实现: