编译原理复习---运行存储分配
适用于电子科技大学编译原理期末考试复习。
1. 运行存储分配策略
编译器在工作过程中,必须为源程序中出现的一些数据对象分配运行时的存储空间。
对于那些在编译时刻就可以确定大小的数据对象,可以在编译时刻就为它们分配存储空间,这样的分配策略称为静态存储分配。
编译时刻分配的存储空间,如何保证在运行起来之后不会与其他程序产生冲突呢(不在编译原理的范畴之内,感兴趣可作了解)?->Linux笔记---进程:进程地址空间-CSDN博客
反之,如果不能在编译时完全确定数据对象的大小,就要采用动态存储分配的策略。即在编译时仅产生各种必要的信息,而在运行时刻,再动态地分配数据对象的存储空间。
动态存储分配包括:栈式存储分配、堆式存储分配。
2. 活动记录
使用过程(或函数、方法)作为用户自定义动作的单元的语言,其编译器通常以过程为单位分配存储空间。过程体的每次执行称为该过程的一个活动(aclivanion)。
过程每执行一次,就为它分配一块连续存储区,用来管理过程一次执行所需的信息,这块连续存储区称为活动记录(activation record)。
一个活动记录通常具有以下几个部分:
临时变量域:用于存放目标程序临时变量的值,例如在计算表达式时产生的中间结果。
局部数据域:用于存放过程本次执行中的局部数据。
机器状态域:用于保存在调用一个过程之前有关机器状态的信息,包括各种寄存器的当前值和返回地址等。
访问链:为访问其它活动记录中所存放的非局部数据提供链地址(在某些语言中需要用到,如PASCAL语言)。
控制链:用以指向主调过程的活动记录。
返回值域:被调用过程用来为主调过程存放返回值的域。
实在参数:用于存放主调过程为被调用过程所提供的实在参数信息(在活动记录中,列出了实在参数的存放空间,但有时参数是通过机器寄存器来传递的,以提高效率)。
排序按照活动记录中各部分的相对地址由低到高的顺序。
3. 变量的存储分配
这是PPT独占的内容,其他地方都没看到。
局部数据 x 的地址:
D表示活动记录的首地址
offset(x): x 在活动记录中的偏移
D+offset(x) 为 x 的地址
局部数据的类型:
静态变量:D 和 offset(x) 在编译时都能确定下来。
半静态变量:offset(x) 在编译时能确定下来,D 在运行时能确定下来。
半动态变量:D和offset(x) 在编译时都不能确定下来,但运行能确定。
动态变量:offset(x) 在编译、运行时都不能确定下来,即 x 的大小在运行时不固定,可动态改变。
仅支持静态变量的语言采用的存储分配方式为静态存储分配。
4. 静态存储分配
在静态存储分配中,编译器为每个过程确定其活动记录在目标程序中的位置。
这样,过程中每个名字的存储位置就确定了,这些名字的存储地址可以被编译到目标代码中。过程每次执行时,它的名字都绑定到同样的存储单元。
适合静态存储分配的语言(即仅支持静态变量)必须满足以下条件:
数组上下界必须是常数
不允许过程的递归调用
不允许动态建立数据实体
静态存储分配的限制很大,PPT上也没有讨论其具体的分配策略,这里就不再多说。
5. 动态存储分配
栈式存储分配:运行时,每进入一个过程,就在栈顶为该过程分配一块数据区,一旦退出该过程,它所占的空间也退还给系统。
堆式存储分配:有些语言允许用户随时动态地申请和释放存储空间,这种策略是让运行程序持有一个大的存区(堆),在申请时从堆中取一块,释放时将一块存储区退还给堆。
我们主要讨论栈式存储分配,即当一个过程被调用时,该过程的活动记录被压入栈;当过程结束时,该活动记录被弹出栈。
下面这些随便看看,理解以下就行。
6. 非局部数据的访问
这里的讨论要分为两种情况:不支持过程嵌套声明的语言 和 支持过程嵌套声明的语言。
6.1 不支持过程嵌套声明的语言
例如C语言,非重点。
过程中使用的数据,要么是自身定义的局部数据,要么是在所有过程之外定义的全局数据。
6.2 支持过程嵌套声明的语言
过程嵌套声明是指在过程中定义过程,例如lambda表达式:
#include <iostream>
// 外部函数
void outerFunction() {
int outerVar = 10;
// 在外部函数内部定义的内部函数
auto innerFunction = [&]() {
// 内部函数可以访问外部函数的变量
std::cout << "Inner function: outerVar = " << outerVar << std::endl;
};
innerFunction();
}
int main() {
outerFunction();
return 0;
}
过程的嵌套深度:不内嵌在任何其它过程中的过程,设其嵌套深度为1。如果一个过程p在一个嵌套深度为i的过程中定义,则设定p的嵌套深度为i+1。
变量的嵌套深度:将变量声明所在过程的嵌套深度作为该变量的嵌套深度。
上面的例子中,outerFunction的嵌套深度为1,innerFunction的嵌套深度为2。
也就是说,一个活动记录 n 的访问链指向的活动记录为:
直接定义了该活动记录对应的过程的过程的活动记录。
从栈顶向栈底查找,最早满足条件一的那一个。
7. 总结
重点:活动记录的组成和他们各自的含义,控制链、访问链的画法。
哈工大的慕课中,关于控制链和访问链还有更多的细节,但是根据PPT的内容,我感觉考试顶多就按照下面的方式来考了:
PPT中的栈比较离谱,是向上生长的,蓝色的线代表控制链,红色的线代表访问链,右部的图反应的是各过程的嵌套深度。