目录
1 运行存储分配概述
编译器在工作过程中,必须为源程序中出现的一些
数据对
象
分配
运行时的存储空间
对于那些在
编译时刻
就可以确定大小的数据对象,可以在
编译时刻就为它们分配存储空间,这样的分配策略称为
静
态存储分配
反之,如果不能在编译时完全确定数据对象的大小,就要
采用
动态存储分配
的策略。即在编译时仅产生各种必要的
信息,而在
运行时刻
,再动态地分配数据对象的存储空间
栈式存储分配
堆式存储分配
静态
和
动态
分别对应
编译时刻
和
运行时刻
![](https://i-blog.csdnimg.cn/blog_migrate/b9fb099a687193556a42692b03e383fa.png)
活动记录
使用
过程
(
或
函数
、
方法
)
作为用户自定义动作的单元的
语言,其编译器通常
以过程为单位分配存储空间
过程体的每次执行称为该过程的一个
活动
(
activation)
过程每执行一次,就为它分配一块连续存储区,用来管理过程一次执行所需的信息,这块连续存储区称为
活动
记录
(
activation record )
![](https://i-blog.csdnimg.cn/blog_migrate/d9dfd65bf1074b10fae96a3d9ac3b297.png)
2 静态存储分配
在静态存储分配中,编译器为每个过程确定其活动记
录在目标程序中的位置。
这样,过程中每个名字的存储位置就确定了
因此,这些名字的存储地址可以被编译到目标代码中
过程每次执行时,它的名字都绑定到同样的存储单元
(1)静态存储分配的限制条件
①数组上下界必须是常数
②不允许过程的递归调用
③不允许动态建立数据实体
满足这些条件的语言有
BASIC
和
FORTRAN
等
(2)常用的静态存储分配方法
①顺序分配法
②层次分配法
3 栈式存储分配
有些语言使用过程、函数或方法作为用户自定义动作的单
元,几乎所有针对这些语言的编译器都把它们的
(
至少一
部分的
)
运行时刻存储以
栈
的形式进行管理,称为
栈式存
储分配
当一个过程被
调用
时,该过程的活动记录被
压入
栈;当过
程
结束
时,该活动记录被
弹出
栈
这种安排不仅允许活跃时段不交叠的多个过程调用之间
共
享空间
,而且允许以如下方式为一个过程编译代码:它的
非局部变量的
相对地址总是固定的
,和过程调用序列无关
活动树
用来描述程序
运行
期间控制进入和离开各个活动的情况
的树称为
活动树
树中的每个
结点
对应于一个
活动
。
根结点
是启动程序执
行的
main过程的活动
在表示过程p
的某个活动的结点上,其
子结点
对应于被
p
的这次活动
调用
的各个过程的活动。按照这些
活动被调
用的顺序
,自左向右地显示它们。
一个子结点必须在其右兄弟结点的活动开始之前结束
![](https://i-blog.csdnimg.cn/blog_migrate/236535fc08a723795c300904255fe22b.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9d93dff566a87753a172ddb42a4594f4.png)
每个
活跃的活动
都有一个位于
控制栈
中的
活动记录
活动树的
根
的活动记录位于
栈底
程序控制所在的活动的记录
位于
栈顶
栈中
全部活动记录的序列
对应于在活动树中
到达当前控制所在的活动结点的路径
![](https://i-blog.csdnimg.cn/blog_migrate/401e76a6780df540f7237b535cc256a8.png)
4 调用序列和返回序列
过程
调用
和过程
返回
都需要执行一些
代码
来管理活动记录
栈,保存或恢复机器状态等
调用序列
实现过程调用的代码段。为一个活动记录在栈中分配空间,并在此记录的字段中填写信息
返回序列
恢复机器状态,使得调用过程能够在调用结束之后继续执行
一个调用代码序列中的代码通常被分割到调用过程(调用者)和被调用过程(被调用者)中。返回序列也是如此
![](https://i-blog.csdnimg.cn/blog_migrate/41140bd8af7212f400c8e92c37bc06d3.png)
变长数据的存储分配
在
现代程序设计语言
中,在
编译时刻不能确定大小的对象
将被分配在
堆
区。但是,如果它们是
过程的局部对象
,也
可以将它们分配在
运行时刻栈
中。尽量将对象放置在栈区
的原因:可以
避免
对它们的空间进行
垃圾回收
,也就
减少
了相应的
开销
只有一个数据对象
局部于某个过程
,且
当此过程结束时它
变得不可访问
,才可以使用栈为这个对象分配空间
![](https://i-blog.csdnimg.cn/blog_migrate/f34377391e0e78de5d460e731d1b8b1a.png)
5 非局部数据的访问
(1)支持&不支持过程嵌套声明的语言
(2)无&有过程嵌套声明时的数据访问
(3)访问链
静态作用域规则:只要过程b
的声明嵌套在过程
a
的
声明中,过程
b
就可以访问过程
a中声明的对象
可以在相互嵌套的过程的活动记录之间建立一种称为
访问链
(
Access link
)
的指针,使得内嵌的过程可以访问外层过程中声明的对象
如果过程b
在源代码中
直接嵌套
在过程
a
中
(
b
的嵌套深度
比
a
的嵌套深度多
1)
,那么
b
的
任何
活动中的访问链都指
向
最近的
a的活动
![](https://i-blog.csdnimg.cn/blog_migrate/9d2a7369bda9a70e303a91b59ac005cc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/11c2600a6e266793dcf2a748f6b37018.png)
![](https://i-blog.csdnimg.cn/blog_migrate/661a47ebbb57d6e9094f7fb85cdca21c.png)
6 堆栈存储分配
堆式存储分配是把连续存储区域分
成块
,当活动记录或
其它对象需要时就分配
块的释放可以按任意次序进行,所以经过一段时间后,
对可能包含交错的
正在使用
和
已经释放
的区域
![](https://i-blog.csdnimg.cn/blog_migrate/f74fe8130ac3333042d969ab4f19a96d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f28582b87acb721a882ca8ac265c09e1.png)
7 符号表
符号表的组织 :
为每个作用域(程序块)建立一个独立的符号表
![](https://i-blog.csdnimg.cn/blog_migrate/ab666465c106045649c5c066e1bea9cc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c398e18152c9445448b31a97e66405c7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1d271f5b3f04536ba56102aac41438cb.png)
8 符号表的建立
![](https://i-blog.csdnimg.cn/blog_migrate/06d43cde452195bf0fde8ef158428630.png)
![](https://i-blog.csdnimg.cn/blog_migrate/0365a55722eebe46a65d11164bcf5616.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cb0ea3b5b4c06e11f0fad366f5d1507d.png)