Skip to content

第十一章 运行时存储空间的组织

Giovanna

About 1592 wordsAbout 5 min

2024-07-04

一、活动记录

一个活动记录是一个程序单元的数据空间;程序单元的一次激活所需的信息管理是通过相应的活动记录来实施的。一个单元的每次激活,都应建立相应的活动记录,它是单元实例的一部分。一个活动记录是一连续的存储区。

image.png

二、变量的类型

静态变量

活动记录、变量的的存储位置在编译时可以确定;不管在程序单元的哪一次活动中,变量均绑定于活动记录中相同的存储位置。

半静态变量

语言允许递归调用,每次激活,分配活动记录。编译时确定相对位置offset(x),单元被激活后,变量x绑定于D+offset(x)。

半动态变量

编译时在活动记录中建立描述符,描述符的大小在编译时可以决定;在单元激活时,才分配它们的空间。

动态变量

描述符及空间大小在编译时不能决定。编译时在活动记录中为动态变量设置两个指针,一个指向该变量的描述符,另一个指向该变量的存储空间。

三、存储分配模式

静态分配

  • 只允许静态变量,变量与存储区域的绑定关系在编译时便可建立,并完成存储分配
  • 不允许递归调用,不允许动态数组
  • 不允许动态类型的数据对象,即不允许有非静态变量

栈式分配

  • 各单元之间的调用关系遵循“后进先出”模式
  • 活动记录的建立和撤销也满足“后进先出”模式
  • 用栈分配活动记录
  • 活动记录的长度或者可静态决定,或者在单元激活时可决定
  • 分配方法:当激活一个程序单元时,其活动记录就动态分配于栈顶

堆分配

由于动态变量表示的数据对象,它的长度、个数都有可能在执行中改变,即在其生存期中动态改变,就不可能在栈上为这样的对象作分配。

Note

一般而言,出现下列情况时,必须用堆分配:

  • 单元活动结束后,局部变量的值还需保留
  • 调用单元与被调用单元的生存期不满足嵌套关系,即出现交叉现象

四、动态连接与静态连接

栈式分配

  • 每个活动记录在程序单元激活时建立在栈顶
  • 指针current指向活动记录的开始;指针free指向栈顶的自由空间;free=current+L,L是活动记录的长度
  • 局部单元通过指针current来引用。若变量X在活动记录中的偏移是i,则它的地址D[current+i]
  • 程序单元退出时,释放栈顶活动记录

1. 动态连接

动态连接:A调用B时,B的活动记录中保存A的活动记录地址

动态链:由动态连接组成的一个调用链

A call E;
E call F;
F call G;
G call F;

image.png

只考虑动态连接,语句call Preturn的翻译:

// call P
D[free]:=ip+5        // 调用语句后第5条语句为返回地址
D[free+1]=current    // 建立动态连接
current:=free        // 调整current
free:=free+L         // 调整free
ip:=P代码段首址       // 控制转入被调用单元

// return
free:=current           // 释放P的当前活动记录
current:=D[current+1]   // 恢复调用单元的活动记录
ip:=D[free]             // 将控制返回调用单元

2. 静态连接

静态作用域规则下非局部环境的引用

Note

动态作用域规则下,静态连接指向动态调用直接外层的活动记录地址;显然,它与动态连接一致。

静态连接:指向嵌套直接外层的最新活动记录的指针,它存储在活动记录的第3个活动单元中

静态链:各嵌套程序单元中的静态连接形成一个链

unit A;
	y:int;
	unit E;
		x:int;
		uint F;
			z:int;
			...
			z:=x+y;
		end F;
		uint G;
			x,y:int;
			...
		end G;
		...
	end E;
end A;

image.png

考虑静态连接,语句call P的翻译:

// call P
D[free]:=ip+6        // 调用语句后第5条语句为返回地址
D[free+1]:=current   // 建立动态连接
D[free+2]:=f(d+1)    // 建立静态连接
current:=free        // 调整current
free:=free+L         // 调整free
ip:=P代码段首址       // 控制转入被调用单元

五、变量作用域

1. 变量的静态作用域规则

最近嵌套原则:

  • 变量x在单元U1中被说明,在x局部于U1。或称x在U1中是可见的
  • 变量x在U1中没有被说明,则x的作用域是包围U1的说明了x的最近嵌套外层单元

2. 变量的动态作用域规则

最近活动规则:

  • 对非局部变量,其引用前应在最近外层中加以说明
  • 这里的最近外层是指动态调用外层,而不是静态嵌套外层

例:对于下列程序,如果采取“静态作用域规则”,则输出结果是 0

如果采用“动态作用域规则”,则输出结果是 1

main() {
	f1() {
		int x=1;
		f2();
	}
	f2() {
		print(x);
	}
	int x=0;
	f1();
}

六、参数传递

形参:被调用单元的参数

实参:调用单元的参数

参数传递的类型:

  • 传值
  • 传值得结果
  • 传地址
void swap(int x,int y){
	printf("%d,%d\n",x,y);
	x=x+y;
	y=x-y;
	x=x-y;
	printf("%d,%d\n",x,y);
}

int main(){
	int a=1,b=2;
	printf("%d,%d\n",a,b);
	swap(a,b);
	printf("%d,%d\n",a,b);
	return 0;
}

1. 传值

单向传参

实参的值=>形参的值

输出结果:

1,2
1,2
2,1
1,2

2. 传参得结果

双向传递

实参的值=>形参的值

形参结果=>实参结果

输出结果:

1,2
1,2
2,1
2,1

3. 传地址

公用同一数据单元

实参地址=>形参地址

1,2
1,2
2,1
2,1