1、存储组织
1.1、运行时内存的划分
在运行时,系统将为目标程序分配一块存储空间,这个存储空间按用途可以划分为下面几个部分:
( 1 )、目标程序区,用来存放所生成的目标程序;
( 2 )、静态数据区,用来存放编译程序本身就可以确定所占用存储空间大小的数据;
( 3 )、运行栈区,在运行时才能分配存储空间的数据就分配在运行栈;
( 4 )、供用户动态申请存储空间的堆区。
这几个部分之间的关系如图7 - 1所示。当然,并不是所有语言的实现都需要这些区域。
图7-1 运行时内存分配示意图
由于编译程序所生成的目标代码的长度在编译时(至迟在连接之后)就能确定,因此可以把它放在一个静态确定的区域内。同样,若一些数据对象的大小在编译时是已知的,它们也可以放入静态确定的存储区域内。应尽可能多地对数据对象进行静态分配,以缩短运行时间。例如,FORTRAN中的所有数据对象都可以进行静态分配。
像PASCAL和C 之类语言的实现,则宜于使用扩充的栈来管理过程的活动(我们将一个过程的每一次执行称为这个过程的一次活动)。当出现一个过程调用时,当前正执行的活动将被打断,有关机器状态的信息,如程序计数器的值、返回地址和机器各寄存器的值,应存入栈中。而当控制从一个被调用过程返回时,在恢复了有关寄存器和程序计数器的值之后,被中断的活动将从断点继续执行。
PASCAL和C 允许数据存储空间在程序控制之下进行分配,这种数据的存储空间可以从堆中得到。
栈和堆可用空白区的大小将随程序的执行而变化。在图7 - 1 中,显示了栈和堆共用一空白存储区,并在使用过程中相互迎面地进行调剂的情况。由于PASCAL和C既存在过程的递归调用又允许动态申请空间,所以这类语言同时需要运行时的栈和堆。
1.2、活动记录
一个过程的一次执行所需信息的管理,是通过使用一个所谓活动记录的连续存储块来实现的。活动记录中可包括如图7 -2 所示的各个域。在PASCAL和C 语言中,我们通常采用以过程为单位的动态存储分配方案,即当一个过程被调用时,就把它的活动记录推入运行时存储栈的桟顶,而在控制返回调用程序时,再从桟顶弹出相应的活动记录。
图7-2 一个常用的活动记录
活动记录中各种域的作用如下:
( 1 )、临时变量域——用来存放目标程序临时变量的值,如计算表达式时所产生的结果;
( 2 )、局部数据域——用来存放过程本次执行中的局部数据、简单变量以及数组内情向量等;
( 3 )、机器状态域——用来保存在调用一个过程之前有关机器状态的信息,其中包括各种寄存器的当前值和返回地址等;
( 4 )、任选的存取链——为访问其它活动记录中所存放的非局部数据提供链地址(这在PASCAL语言中是需要用到的);
( 5 )、任选的控制链——用以指向主调过程的活动记录;
( 6 )、实在参数——用于存放主调过程为被调用过程所提供的实在参数信息(在活动记录中,我们列出了实在参数的存放空间,但是为了提高效率,有时参数是通过机器寄存器来传递的);
( 7 )、返回值域——被调用过程用来为主调过程存放返回值的域。
每个活动记录都可分为定长部分和可变部分。定长部分用来存放那些在编译时就能确定其体积的量,如简单变量、常界数组等;可变部分用来存放只有在运行时,才能确定其体积的量,如可变数组、动态指针等。虽然只有在运行时,才能为这些可变体积的数据在活动记录的可变部分分配其存储空间,但在编译时却能产生通过活动记录的首地址(一般用一个指示器指示)来访问它们的目标代码,这是因为在该活动记录的定长部分,已设定了存放确定其体积的有关信息的域(如数组的内情向量),而这些域在活动记录中的相对位置是恒定的。