第8章 存储空间的分配
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
printd(int n) { int i; if(n<0) {putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10+’0’); }
top
简单变量:i=1
形参:n=12
参数个数:1 sp top
返回地址 老sp:k1
简单变量:i=12
临时变量 数组 简单变量
…
新的Байду номын сангаас动记录
数组 临时变量
数组内情向量
变,则不能确 定活动记录的 大小,但要求 在编译时一定 要能确定活动 记录的长度, 为此,将活动 记录改为:
简单变量
…
活 动 记 录
30
对数组建立内情向量,记录已知信息。 如:
数组起始地址a 维数n 数组常量C d1 d2 … dn 数组起始地址a 维数n 数组常量C L1 N1 d1 L2 N2 d2 … … Ln Nn dn 这样便于计算数组越界
k1
27
top
printd(int n) { int i; if(n<0) {putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10+’0’); }
简单变量:i=0
形参:n=1
参数个数:1
返回地址
sp 老sp:k2 简单变量:i=1
动态数据区
由动态存储分配产生的数据区。 这种数据区不是固定不变的。随着相应 程序单位的调用和返回,它也会进入和退 出。
8
静态存储分配对程序设计语言的要求:
不允许有递归调用;
不允许有嵌套的子程序;
不允许有可变数组。
FORTRAN语言满足这些要求。
9
变量地址= 相对地址 + 起始地址
top
简单变量:i=12 形参 形参 :n=-125 :n=125
进入子过程体内,填简单变量i 参数:n=-125<0,先打印’-’,n=125; sp i=125/10=12!=0 : printd(12) top
sp
参数个数:1
返回地址 老sp:K
k1
…
25
①进入子过程体前: (1+3)[top]=Ti=n=12 1[top]=sp=k1 3[top]=参数个数=1 ②转入子过程体 sp=top+1 1[sp]=返回地址 top=top+L
k3
top
形参:n=12
参数个数:1 sp
返回到第2次调用处(包括返回数 据区): n=12,打印:12%10+‘0’=‘2’
返回地址 老sp:k1
简单变量:i=12
k2
形参:n=125 参数个数:1 返回地址 老sp:k …
k1
28
printd(int n) { int i; if(n<0) {putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10+’0’); }
31
Void p(…) { … int A[1..m]; int B[1..n]; … }
top
数组B 数组A P的活动记录 …
sp
32
计算各维的长度(或上、下限)
调用数组空间分配子程序,其参数为
各维的长度(或上、下限)及内情向 量的起始地址。
33
填写内情向量中的信息
(数组起址:=top+1 )
17
top sp top sp
Q的数据区 R的数据区
Q的数据区
全局变量
当 Q 退出后, SP 应指到什 么地方不知道(因为 R 的 长度不知),因此在每个 数据区设一老 SP 记下上一 过程的SP值。
18
设a为数据区中的相对地址, 则a[sp]为相对地址a+sp的地址;
…
20
简单变量
…
则20[sp]:将20单元 的简单变量取出来。
调整top:top:=top+v v为数组体积
top top
数组 P的活动记录 …
sp
34
例:
procedure P procedure Q procedure R
这种过程嵌套结构所附 带的语义检查: ①过程调用的合理性 ②变量使用的合法性
end end
end
35
由于过程定义是嵌套的,任一个过程都 可以引用其外层过程中定义的变量。 因此,子过程必须要知道其全部外层过 程的最新活动记录的地址。 由于允许递归调用和包含可变数组,因 此过程的活动记录位置往往是变化的, 因此应跟踪每个外层过程的最新活动记 录的位置。
12
栈式存储分配适合于:
层次嵌套结构
允许过程递归调用
含可变数组
的程序设计语言。
13
数据区内容
临时变量 数组 简单变量 形式单元 参数个数 返回地址
将这样一个数据区称为 一个活动记录。 活动记录的大小在编译 时可以静态地确定。
14
活动数据区的内容与静态存储分配的数 据区相同, 所不同的是要允许递归,则每调用一次 过程都要生成一个数据区,每次操作都 是在新的数据区上进行, 递归调用多少次就生成多少个数据区 由于只有在运行时才知道调用多少次, 因此只能在运行时确定分配多少空间。
top sp
老sp
…
进入P后,首先应定义新的活动记录的 sp,然后保存返回地址并定义新的top值
…
20
对应的目标动作:②进入P后的动作:
sp:=top+1; //定义新sp
top Add(T3)
1[sp]:=返回地址;//保存返回地址 top:=top+L; //定义新的top
过程P的活动记录长 度,在编译时可静 态计算出来。
活动记录内容 运行时,每当进入一个过程就 top 临时变量 将此过程的数据区(活动记录 数组 )置于栈顶。 简单变量 当过程执行完毕后,该过程所 形式单元 对应的数据区也不复存在,进 参数个数 入到下一个将要执行的过程的 数据区中。 返回地址 因此,有必要用两个寄存器来 sp 老sp(控制链) 记下数据区的大小。 现行过程活动记录起点
top
简单变量:i=1
形参:n=12
参数个数:1 sp top
返回地址 老sp:k1
简单变量:i=12
k2
返回到第1次调用处(包括返回数 据区): n=125, 打印:125%10+‘0’=‘5’ 总的打印结果为:-125
sp
形参:n=125 参数个数:1 返回地址 老sp:k …
k1
29
原来的活动记录 若数组长度可
Add(T2) Add(T1)
参数个数:3 返回地址 sp top sp
老sp
…
…
21
对应的目标动作: ③从P返回时的动作:
top:=sp-1; sp:=0[sp]; //老sp 无条件转2[top] //返回地址
top
Add(T3)
Add(T2) Add(T1)
参数个数:3
返回地址
sp top sp
简单变量:i=0
形参:n=1
参数个数:1
返回地址
sp 老sp:k2 简单变量:i=1
k3
形参:n=12
参数个数:1
进入过程体内: 参数n=1 i=1/10=0; putchar打印:1%10+‘0’=‘1’;
返回地址 老sp:k1
简单变量:i=12
k2
形参:n=125 参数个数:1 返回地址 老sp:K …
3
编译程序必须分配目标程序运行时的 存储空间。这些存储空间包括:
用户定义的各类变量和常量所需存储单 元; 作为保留中间结果的参数传递用的临时 工作单元; 调用过程或函数时需要的形式单元; 返回地址以及组织输入 / 输出所需的缓冲 区。
4
从本质上看,数据对象的空间分配是将 程序中的每个对象名字与一个存储位置 进行绑定。 目标程序运行时的数据空间组织的基本 依据是程序设计语言对程序运行中存储 空间的使用和管理办法的规定。
临时变量 数组 简单变量 形式单元 寄存器保护区 返回地址
10
很多高级语言都支持嵌套分程序、嵌 套或递归过程结构及动态数组, 这些需要运行时存储空间管理机制。 栈式存储分配方法是适合于这类语言 的一种存储管理方法。 栈式存储分配方法是把整个程序的存 储空间都安排在一个栈内。
11
进入主程序时,将主程序定义的各类全程 量所需存储空间分配于栈顶; 每当调用一个子程序(过程 /函数)时,就 将其所需的存储空间分配于栈的当前栈顶; 每当从子程序运行结束时,就从栈中释放 其所占空间; 整个程序执行完后,释放它所占用的全部 空间。
如:递归调用的过程,在运行时,其同一 个局部名字应对应不同的运行空间位置。
5
过程是否允许递归? 当控制从一个过程的活动返回时,对局部 如何处理? 过程是否允许引用非局部名? 过程调用时如何传递参数;过程是否可以 作为参数被传递和作为结果被返回? 存储空间可否在程序控制下进行动态分配? 存储空间是否必须显式地释放?
老sp
…
…
22
动态存储分配比静态存储分配省空间。
因为当某一过程执行完后就可以释放它
所占的空间,只有当都不释放空间时才
与静态分配所用空间相同。
但动态分配省空间所付出的代价是运行
时分配,降低了执行效率。
23
某含递归调用的C语言程序: printd(int n) { int i; if(n<0) { putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10 +‘0’); } //打印余数 在主程序中调用:printd(-125)
36
静态链
指向其直接外层过程的最新活动记录的 基地址。
Display表
每当进入一个子过程时,在建立它的活 动记录区的同时建立一张嵌套的层次显 示表——display。
37
层次
0
Program P; Var a,x: integer; 1 Procedure Q(b:int ) Var i:integer; 2 Procedure R( u:int; Var v:int); Var c,d: integer begin if u=1 then R(u+1,v)
sp
老sp(控制链)
19
过程调用四元式: 对应的目标动作: (par,_,_,T1) (par,_,_,T2) (par,_,_,T3) (call,_,_,p)
①进入P前的动作:
Add(T3) Add(T2)
Add(T1)
参数个数:3
//地址调用 (i+3)[top]:=add(Ti) //值调用 (i+3)[top]:=Ti 1[top]:=sp;//保存老sp 3[top]:=参数个数
24
①进入子过程体前: (1+3)[top]=Ti=n=-125 1[top]=sp=k //保留老sp 3[top]=参数个数=1 ②转入子过程体 sp=top+1 1[sp]=返回地址 top=top+L
printd(int n) { int i; if(n<0) {putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10+’0’); }
6
静态存储分配
在编译阶段进行的存储分配。 对程序中的简单变量和常量数组,由于它们 所需的存储单元数在编译时就可以确定,因 此在编译时就可以给它们分配存储单元。
动态存储分配
在运行阶段才能确定数据的位置及大小的分 配方法。
7
静态数据区
由静态存储分配产生的数据区。 这种数据区在整个程序运行过程中是固 定不变的。
第八章 运行时的存储空间组织
8.1 概述 8.2 静态存储分配 8.3 动态存储分配
2
编译程序的最终目的是将源程序翻译 成等价的目标程序。 因此,除了进行词法、语法、语义分 析外,在生成目标代码前,需要把程序 静态的正文与程序运行时的活动联系起 来,弄清楚在代码运行时刻,程序中的 各种变量、常量等是如何存放的,如何 去访问它们。
15
Program main; 全局变量 procedure R; … end;(R) procedure Q; … call R; end;(Q) … 主程序; call Q; … end.(main)
设在主程序中调用Q, Q又调用了R,则栈式 分配为:
R的数据区 Q的数据区 主程序的数据区
每调用一个过程就开辟一 个数据区。当某个过程执 行完后,退出此过程的数 据区,进入下一个过程的 数据区。 16
k2
进入子过程体内,填简单变量i i=12/10=1!=0 : printd(1)
sp
形参:n=125 参数个数:1 返回地址 老sp:K …
k1
26
top
printd(int n) { int i; if(n<0) {putchar(‘-’); n=-n; } if((i=n/10)!=0) printd(i); L:putchar(n%10+’0’); }