编译第10章
第10章目标程序运行时的存储组织
p3活动记录 存取链(静态链) 控制链(动态链)
p3活动记录 存取链(静态链) 控制链(动态链)
main活动记录
2、用Display表
Display表---嵌套层次显示表 当前激活过程的层次为K,它的Display表含有K+1个
单元,依次存放着现行层,直接外层…直至最外层的每 一过程的最新活动记录的基地址。 说明:1、由于过程的层数可以静态确定,因此每个过程 的Display表的体积在编译时即可以确定。
Q的 活动记录
P的 活动记录 主程序的 活动记录
DISPLAY表பைடு நூலகம்维护和建立
为便于组织存储区,将display作为活动记录的一 部分,其相对地址在编译时是完全可以确定的。
假设过程P1可调用P2,为了能在P2中建立P2的 display,在P1调用P2时设法把P1的display地址 作为连接数据之一(全局display地址)传送给P2, 因此连接数据包括: 老SP值(动态链) 返回地址 全局display地址
嵌套过程的栈式分配方案
分程序结构的存储分配方案
3、过程活动:一个过程的活动指的是该过程的一次执行。
4、活动记录:一个过程的一次执行所需要的信息使用一个连 续的存储区来管理,这个区(块)叫做一个活动记录。
活动记录一般包含:
(1)连接数据
返回地址—调用过程指令的下一条指令的地址。
动态链(控制链)—指向调用该过程活动记录地址的指针。用 于当调用返回时,将当前栈顶正确切换到调用者的活动记录
2、某过程p是在层次为i的过程q内定义的,并且q是 包围p的直接外层,那么p的过程层数为i+1。
例: program main(i,0);
第十章 指针
19
10.4 字符串与指针
字符串的表示形式
1. 可以用字符数组表示字符串
main() { char string[]=”I love China!”; printf(“%s\n”, string); }
2. 可用字符指针变量来表示
main() { char *string=”I love China!”; printf(“%s\n”, string); }
9
10.2.2 指针变量的引用
& :取地址运算符 * :指针运算符
i_pointer-----指针变量,它的内容是地址量 Eg10.1 *i_pointer----指针的目标变量,它的内容是数据 &i_pointer---指针变量占用内存的地址 main() &*i_pointer等价于i_pointer { (&*i_pointer)++与&*i_pointer++的区别 int a,b; int *pointer_1,*pointer_2; a=100;b=10; pointer_1=&a; pointer_2=&b; printf("%d,%d\n",a,b); printf("%d,%d\n",*pointer_1,*pointer_2); }
21
10.5 指向函数的指针
赋值 函数名代表该函数的入口地址。因此,可用 函数名给指向函数的指针变量赋值。 指向函数的指针变量=[&]函数名;
注意:函数名后不能带括号和参数;函数名前的 “&”符号是可选的。
调用格式 (*函数指针变量)([实参表])
22
用指向函数的指针作函数参数
C--程序设计--第10章-多态性及虚函数
使用重载函数注意:
不要使用重载函数描述不相干的函数 在类中,构造函数和普通成员函数均可以
重载 避免与函数的默认参数产生二义性
二、运算符重载
运算符重载(operate overloading)就是 赋予已有的运算符多重含义。
运算符重载实质是函数重载,运算符重载 的选择与函数重载类似,会根据运算符的 操作数的类型、个数和顺序来进行运算符 函数的选择。
#include<iostream.h> str#iinngc:l:usdter<isntgr(icnhga.rh>*s) v{}ossccsssc{s{{ittohtttolsstlsssls*drruarrrueptrepttepsi1trii3tc{pn=rin=rrn=pmn.<nn.<lprgncngncign=agp<*ggp<auitepgtepnte'irssrssbv\hwy:hwyghwnsit1ssitsla0=(:=(:=(tnr=ttnrit'scssscs:sc)rt1"rrt3scesss~ivci;thpt1hpsih1(.T23(.t::tttsnohn}ra,r.a,tza()gh(()grrrrttiatlrsilrsrer";eass;eiiiirdre[)ne[1i;[Ttt1ttnnnniglnl;gnl.nlhl)rlggggnep*e(e}(gesgeiei;2e(((gtrsnsnstnp(nsns)ncsi(lipg)gthg)ig(;(htn)en;t;tr;t;nti)a)artnthhih}ths<<ri{((;+n++<p<snd))}1g1s1aere*ige;]]i]nonszl{{;&;z;ddgd)&eercseelrl;s=teo1)m;a;/18etu)om/)0ut..;)构sr<""/;pn<造);//;s;/复}lp函构e<制n<数造ge构tn函hd造;l数};重} 载
c语言 ●第10章 指针-1
…
19
2.定义时同时赋值
int a[10];
int *p=a; c规定: /* 相当于int *p=&a[0] */
若有 int a[10];
int *p=a; 则 p+1:指向下一个数组元素。
…
p+i:其指向下移i个元素。
20
说明:若有 int a[10]; int *p=a; (1) p+i *(p+i) = &a[i] a[i]= a+i *(a+i) (2)数组的指针变量也可带下标 a[i] ,p[i], *(a+i),*(p+i) 是等价的。 (3)a与p的区别:a代表数组a的首地址,是常量。 p=a; p也代表数组a的首地址,是变量。 如:p++; 是正确的,而 a++; 是错误的。 (4)引用数组元素有三种方法: 下标法: a[i]或p[i] 地址法:*(a+i) 效率低 指针法:*(p+i) *p++ 效率高
13
讨论: 若将被调函数swap( )改为: swap(int *p1,int *p2) {int *p; *p=*p1; *p1=*p2; *p2=*p; /*中间变量是指针变量所指的对象*/ } p无确定的地址(地址是随机的),可能指向任何单 元,有可能破坏系统(乱放枪)。加上int c;p=&c;就没 有问题了。
3 6 9 …
i j k
2004
3010
2000
i_pointer
3
二.对内存单位的访问 存数—写 取数—读 对内存单位的访问,是通过地址进行的。 如: printf(“%d”,i); 读 再如:scanf(“%d”,&i); 写 直接访问:按变量的地址直接读写变量的值。 如:k=i+j; (1)从2000开始的内存单元中取出i的值3. (2)从2002开始的内存单元中取出j的值6. (3)相加后,送入2004开始的内存单元。 间接访问:将变量a的地址存入另一变量b中,访问a时,先 找b,取出a的地址,再按此地址访问a。
第10章 预处理命令
第十章预处理命令所谓编译预处理是指,在对源程序进行编译之前,先对源程序中的编译预处理命令进行处理;然后再将处理的结果,和源程序一起进行编译,以得到目标代码。
一、宏定义与符号常量在C语言中,“宏”分为无参数的宏(简称无参宏)和有参数的宏(简称有参宏)两种。
无参宏定义1.无参宏定义的一般格式#define 标识符字符串其中:“define”为宏定义命令;“标识符”为所定义的宏名,通常用大写字母表示,以便于与变量区别;“字符串”可以是常数、表达式、格式串等。
2.使用宏定义的优点(1)可提高源程序的可维护性(2)可提高源程序的可移植性(3)减少源程序中重复书写字符串的工作量例9.1 输入圆的半径,求圆的周长、面积和球的体积。
要求使用无参宏定义圆周率。
/*程序功能:输入圆的半径,求圆的周长、面积和球的体积。
*/#define PI 3.1415926 /*PI是宏名,3.1415926用来替换宏名的常数*/void main(){float radius,length,area,volume;printf("Input a radius: ");scanf("%f",&radius);length=2*PI*radius; /*引用无参宏求周长*/area=PI*radius*radius; /*引用无参宏求面积*/volume=PI*radius*radius*radius*3/4; /*引用无参宏求体积*/printf("length=%.2f,area=%.2f,volume=%.2f\n", length, area, volume);}3.说明(1)宏名一般用大写字母表示,以示与变量区别。
但这并非是规定。
(2)宏定义不是C语句,所以不能在行尾加分号。
否则,宏展开时,会将分号作为字符串的1个字符,用于替换宏名。
(3)在宏展开时,预处理程序仅以按宏定义简单替换宏名,而不作任何检查。
fortran第10章收集资料
共享的内部过程
END FUNCTION
END MODULE EXAM_MIDULE PROGRAM AVER_MAX3
引用模块
USE EXAM_MODULE READ *,A,B,C PEINT *,AVER(),MAX3() ENDPROGRAM
内部过程优先, !定义主程序调用模块过程
Fortran 90
各单位共享信息的途径: ①虚实结合 ②模块单位 1.功能:提供大量共享符号常量、变量、派生类 型定义和过程;即公用的信息可放入模块单位
Fortran 90
2.模块单位写法:一般形式
MODULE 模块名 !模块单位定义语句 类型说明部分
[CONTAINS 内部过程子程序1
…… 内部过程子程序n] END MODULE [模块名]
AVER_VALUE=(A+B+C)/3.0 END FUNCTION FUNCTION MAX3() RESULT(MAX_VALUE)
USE EXAM_MODULE
USE 语句引 用
REAL ::AVER_VALUE
MAX_VALUE=A
IF(B>MAX_VALUE)MAX_VALUE=B
IF(C>MAX_VALUE)MAX_VALUE=C
程序阅读:先读模块单位及各单位的USE, 画共享表(按名结合),再读主程序.
第10章-编译预处理ppt课件(全)
-8-
带参数的宏定义(续)
【例10-3】用宏来定义多个语句的例子。
宏的使用有很多好处,不仅可以简化程序的书写,而且便于程序的 修改和移植,使用宏名来代替一个字符串,可以减少程序中重复书写某 些字符串的工作量。
根据宏定义中是否有参数,可以将宏分为不带参数的宏定义与带参 数的宏定义两种,下面分别讨论这两种宏的定义与调用。
-4-
10.1.1 不带参数的宏定义
不带参数的宏的宏名后面没有参数,不带参数的宏定义又称简单宏 定义。其定义的一般形式为:
宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表 达式。在宏替换时,用该字符串代换宏名。根据宏定义中是否有参数,可以将宏分 为不带参数的宏定义与带参数的宏定义两种。在写带有参数的宏定义时,宏名与带 括号参数间不能有空格,否则将空格以后的字符都作为了替换字符串的一部分,这 样就变成不带参数的宏定义了。不要把带参数的宏定义与带参数的函数混淆,带参 的宏定义在预处理时只是字符串的替换,而带参的函数却是将实参的值一一对应的 传递给形参。
#define 宏名 字符串 其中,“#”表示预处理命令。define是关键字,表示该命令为宏定 义。为了与一般的普通变量相区别,宏名一般使用大写。“字符串”一 般为常量、表达式或字符串。 在进行预处理时,系统会将程序中的“宏名”用“字符串”来替换。
-5-Biblioteka 10.1.1 不带参数的宏定义
C语言程序设计第10章文件及其应用
学一学
1.定义文件指针 一般形式为: FILE * fp; 其中fp就是所定义文件指针。 FILE类型 以及所有的文件读写函数和相关常量都定 义在文件stdio.h中,在源程序的开头要 包含头文件 stdio.h 。
学一学
2.打开文件 C语言中,使用fopen函数来打开文件。打开文件是使 一个文件指针变量指向被打开文件的结构变量,以便通 过该指针变量访问打开的文件。fopen函数的调用形式 如下: fopen(chFileName,mode); 以mode 方式打开文件chFileName。其中,参数 chFileName 是将要读写文件的文件名,mode为文件 的操作方式。若文件打开成功,返回一个文件指针,若 打开失败,则返回空值NULL,NULL在stdio.h中被定 义为0。文件操作方式mode是一个整数,其取值及含 义如表10-1所示。
流程图
开始 定义文件指针fp,定义整型数组 iArray[5],定义循环变量i 以写二进制文件方式打开文件 fp=fopen("test.dat","wb") N i=0
fp==NULL Y
i<5 Y 输入1个整数存入 数组元素iArray[i]
N
i++
把数组iArray中5 个整数写入文件
显示出错信息 关闭文件 exit(0)
试一试
问题10.1编一程序从键盘输入一串字符“may friendship forever! ”,然后保存在文件myInfo.txt中。 【解题步骤】 1.定义文件指针fp; 2.定义字符数组chInfo用来存输入的字符串; 3.以写文本文件方式打开文件myInfo.txt; 4.如果打开文件失败,则输出错误信息并结束程序; 5.否则,打开文件成功,则从键盘输入数据; 5.将字符数组chInfo中的字符写入文件myInfo.txt; 6.关闭文件。
程序设计语言编译原理第三版第10章
§10.2 局部优化
举例:考察下面的三地址代码程序
(1)Read X
(2)Read Y
B1
(3)R:=X mod Y (4)if R=0 goto (8) B2
(5)X:=Y
(6)Y:=R
B3
(7)goto(3)
(8)write Y B4
(9)halt
B1
B2
B3
B4
§10.2 局部优化
3.流图及其生成
标识符(包括常数)-结点 NODE(A)-描述上述对应关系的函数,其值或者是一个结点的编号,
或者无定义
(2)中间代码的三种形式:A:=B A:=op B A:=B op C 或 A:=B[C]
(3)构造算法: ①开始,DAG为空 ②对基本块中每一条中间代码式,依次执行以下步骤:
§10.2 局部优化
步骤: 1.如果NODE(B)无定义,则构造一标记为B的叶结点并定义
NODE(B)为这个结点 如果当前代码是0型,则记NODE(B)的值为n,转4 如果当前代码是1型,则转2(1) 如果当前代码是2型,则(ⅰ)如果NODE(C)无定义,则构造一标 记
为C的叶结点并定义NODE(C)为这个结点;(ⅱ)转2(2)
(1)T0:=3.14 (2)T1:=2*T0 (3)T2:=R+r (4)A:=T1*T2 (5)B:=A (6)T3:=2*T0 (7)T4:=R+r (8)T5:=T3*T4 (9)T6:=R-r (10)B:=T5*T6
(4)代数变换
§10.2 局部优化
二、基本块的DAG表示及其应用
1.基本块的DAG:
一种结点带有下述标记或附加信息的DAG
(1)图的叶结点以一标识符(变量名)或常数作为标记,表示该 结点代表该变量或常数的值。
1编译原理 第三版 陈火旺 课后习题及答案
第2章习题参考答案P36-6 (1)L G ()1是0~9组成的数字串(2) 最左推导:N ND NDD NDDD DDDD DDD DD D N ND DD D N ND NDD DDD DD D ⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒0010120127334556568最右推导:N ND N ND N ND N D N ND N D N ND N ND N D ⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒⇒77272712712701274434886868568P36-7 G(S)O N O D N S O AO A AD N→→→→→1357924680|||||||||||P36-8文法:E T E T E T TF T F T F F E i→+-→→|||*|/()| 最左推导:E E T T TF T i T i T F i F F i i F i i i E T T F F F i F i E i E T i T T i F T i i T i i F i i i ⇒+⇒+⇒+⇒+⇒+⇒+⇒+⇒+⇒⇒⇒⇒⇒⇒+⇒+⇒+⇒+⇒+⇒+********()*()*()*()*()*()*()最右推导:E E T E TF E T i E F i E i i T i i F i i i i i E T F T F F F E F E T F E F F E i F T i F F i F i i i i i ⇒+⇒+⇒+⇒+⇒+⇒+⇒+⇒+⇒⇒⇒⇒⇒+⇒+⇒+⇒+⇒+⇒+⇒+**********()*()*()*()*()*()*()*()语法树:/********************************EE FTE +T F F T +iiiEEFTE-T F F T -iiiEEFT+T F FTiii*i+i+ii-i-ii+i*i*****************/P36-9句子iiiei 有两个语法树:S iSeS iSei iiSei iiiei S iS iiSeS iiSei iiiei ⇒⇒⇒⇒⇒⇒⇒⇒P36-10/**************)(|)(|S T TTS S →→***************/P36-11/*************** L1:ε||cC C ab aAb A AC S →→→ L2:bcbBc B aA A AB S ||→→→εL3:εε||aBb B aAb A AB S →→→ L4:AB B A A B A S |01|10|→→→ε ***************/第2章习题参考答案P64–7(1)101101(|)*1 ε ε 1 0 11 确定化:0 1 {X} φ {1,2,3} φ φ φ {1,2,3} {2,3} {2,3,4} {2,3} {2,3} {2,3,4} {2,3,4} {2,3,5} {2,3,4}{2,3,5} {2,3} {2,3,4,Y} {2,3,4,Y}{2,3,5}{2,3,4,}1 00 0 1 1 0X 1 2 3 4 Y5 XY0 12 30 10 1 1 1 最小化:{,,,,,},{}{,,,,,}{,,}{,,,,,}{,,,}{,,,,},{},{}{,,,,}{,,}{,,,},{},{},{}{,,,}{,012345601234513501234512460123456012341350123456012310100==== 3012312401234560110112233234012345610101}{,,,}{,,}{,},{,}{},{},{}{,}{}{,}{,}{,}{}{,}{}{},{},{,},{},{},{}===== 010 0 1 00 1 0 1 1 1P64–8(1)01)0|1(*(2))5|0(|)5|0()9|8|7|6|5|4|3|2|1|0)(9|8|7|6|5|4|3|2|1(*(3)******)110|0(01|)110|0(10P64–12(a)aa,b a65 4 5 01 2 4 3 01确定化:a b {0} {0,1} {1} {0,1} {0,1} {1} {1} {0} φ φφφ给状态编号:a b 0 1 2 1 1 2 2 0 3 333aaa b b bba最小化:{,},{,}{,}{}{,}{}{,}{,}{,}{}{,},{},{}012301101223032330123a ba b ====a ab bab (b)b b aa ba0 1 2 3 01 2 0 2 3a bb aa a已经确定化了,进行最小化 最小化:{{,}, {,,,}}012345011012423451305234523452410243535353524012435011012424{,}{}{,}{,}{,,,}{,,,}{,,,}{,,,}{,}{,}{,}{,}{,}{,}{,}{,}{{,},{,},{,}}{,}{}{,}{,}{,}a b a b a b a b a b a =============={,}{,}{,}{,}{,}{,}{,}10243535353524 b a bb b aa baP64–14(1) 01 0 (2):(|)*0100 1 ε ε14 5 0 1 2 01YX YX2 1确定化:0 1 {X,1,Y} {1,Y} {2} {1,Y} {1,Y} {2} {2} {1,Y} φ φφφ给状态编号:0 1 0 1 2 1 1 2 2 1 3 3330 1 01 1 10 最小化:{,},{,}{,}{}{,}{}{,}{,}{,}{}{,},{},{}0123011012231323301230101====1 1 1 0第4章课后习题答案P81–1(1) 按照T,S 的顺序消除左递归ε|,)(||^)(T S T T S T T a S S G '→''→→'递归子程序:0 2 13 01 3procedure S; beginif sym='a' or sym='^' then abvance else if sym='(' then begin advance;T;if sym=')' then advance; else error; end else error end;procedure T; begin S;'T end;procedure 'T ; beginif sym=',' then begin advance; S;'T end end; 其中:sym:是输入串指针IP 所指的符号 advance:是把IP 调至下一个输入符号 error:是出错诊察程序 (2)FIRST(S)={a,^,(} FIRST(T)={a,^,(} FIRST('T )={,,ε} FOLLOW(S)={),,,#} FOLLOW(T)={)} FOLLOW('T )={)} 预测分析表a^() , # S S a →S →^S T →()TT ST →' T ST →' T ST →''T'→T ε '→'T ST ,是LL(1)文法P81–2文法:|^||)(|*||b a E P F F F P F T T T F T E E E T E →'→''→→''→+→''→εεε(1)FIRST(E)={(,a,b,^} FIRST(E')={+,ε} FIRST(T)={(,a,b,^} FIRST(T')={(,a,b,^,ε} FIRST(F)={(,a,b,^} FIRST(F')={*,ε} FIRST(P)={(,a,b,^} FOLLOW(E)={#,)} FOLLOW(E')={#,)} FOLLOW(T)={+,),#} FOLLOW(T')={+,),#}FOLLOW(F)={(,a,b,^,+,),#} FOLLOW(F')={(,a,b,^,+,),#} FOLLOW(P)={*,(,a,b,^,+,),#} (2)考虑下列产生式:'→+'→'→'→E E T T F F P E a b ||*|()|^||εεεFIRST(+E)∩FIRST(ε)={+}∩{ε}=φ FIRST(+E)∩FOLLOW(E')={+}∩{#,)}=φ FIRST(T)∩FIRST(ε)={(,a,b,^}∩{ε}=φ FIRST(T)∩FOLLOW(T')={(,a,b,^}∩{+,),#}=φ FIRST(*F')∩FIRST(ε)={*}∩{ε}=φFIRST(*F')∩FOLLOW(F')={*}∩{(,a,b,^,+,),#}=φ FIRST((E))∩FIRST(a) ∩FIRST(b) ∩FIRST(^)=φ 所以,该文法式LL(1)文法. (3)+ * ( ) a b ^ # EE TE →'E TE →' E TE →' E TE →'E' '→+E E'→E ε'→E εTT F T →'T F T →' T F T →' T F T →'T''→T ε'→T T '→T ε '→T T '→T T '→T T '→T εFF P F →' F P F →' F P F →' F P F →'F' '→F ε '→'F F * '→F ε '→F ε '→F ε '→F ε '→F ε '→F εPP E →() P a → P b → P →^(4)procedure E; beginif sym='(' or sym='a' or sym='b' or sym='^' then begin T; E' end else error endprocedure E'; beginif sym='+'then begin advance; E endelse if sym<>')' and sym<>'#' then error endprocedure T; beginif sym='(' or sym='a' or sym='b' or sym='^' then begin F; T' end else error endprocedure T'; beginif sym='(' or sym='a' or sym='b' or sym='^' then Telse if sym='*' then error endprocedure F; beginif sym='(' or sym='a' or sym='b' or sym='^' then begin P; F' end else error endprocedure F'; beginif sym='*'then begin advance; F' end endprocedure P; beginif sym='a' or sym='b' or sym='^' then advanceelse if sym='(' thenbeginadvance; E;if sym=')' then advance else error endelse errorend;P81–3/***************(1) 是,满足三个条件。
《编译原理》(陈火旺版)课后作业参考答案ch6-10
第6章 属性文法和语法制导翻译7. 下列文法由开始符号S 产生一个二进制数,令综合属性v al 给出该数的值:试设计求S.val 的属性文法,其中,已知B 的综合属性c, 给出由B 产生的二进位的结果值。
例如,输入101.101时,S.val=5.625,其中第一个二进位的值是4,最后一个二进位的值是0.125。
【答案】11. 设下列文法生成变量的类型说明:(1)构造一下翻译模式,把每个标识符的类型存入符号表;参考例6.2。
【答案】第7章 语义分析和中间代码产生1. 给出下面表达式的逆波兰表示(后缀式):3. 请将表达式-(a+b)*(c+d)-(a+b+c)分别表示成三元式、间接三元式和四元式序列。
【答案】间接码表:(1)→(2)→(3)→(4)→(1)→(5)→(6)4. 按7.3节所说的办法,写出下面赋值句A:=B*(-C+D ) 的自下而上语法制导翻译过程。
给出所产生的三地址代码。
【答案】5. 按照7.3.2节所给的翻译模式,把下列赋值句翻译为三地址代码: A[i, j]:=B [i, j] + C[A [k, l]] + d [ i+j] 【答案】6. 按7.4.1和7.4.2节的翻译办法,分别写出布尔式A or ( B and not (C or D) )的四元式序列。
【答案】用作数值计算时产生的四元式: 用作条件控制时产生的四元式:其中:右图中(1)和(8)为真出口,(4)(5)(7)为假出口。
7. 用7.5.1节的办法,把下面的语句翻译成四元式序列: While A<C and B<D do if A=1 then C:=C+1else while A ≦D do A:=A+2; 【答案】第9章 运行时存储空间组织4. 下面是一个Pascal 程序:当第二次( 递归地) 进入F 后,DISPLAY 的内容是什么?当时整个运行栈的内容是什么? 【答案】第1次进入F 后,运行栈的内容: 第2次进入F 后,运行栈的内容:第2次进入F 后,Display 内容为:5. 对如下的Pascal 程序,画出程序执行到(1)和(2)点时的运行栈。
编译原理第十章目标程序运行时的存储组织
编译原理第十章目标程序运行时的存储组织课前索引【课前思考】◇回顾通常的编译过程,能否找到本章所讲内容在哪个过程?◇为什么编译程序要考虑目标程序运行时存储区的管理与组织?◇请归纳C语言与PASCAL语言的程序结构与数据类型的不一致点【学习目标】全面熟悉目标程序运行时存储区的整体布局;每种存储区的组织方式与管理方法;并通过实例着重掌握,对同意过程嵌套定义的情况,栈式动态存储分配的组织方式与运行时进栈退栈的活动实现方法。
【学习指南】在代码生成前,编译程序务必进行目标程序运行环境的配置与数据空间的分配。
通常来讲,假如编译程序从操作系统中得到一块存储区以使目标程序在其上运行,该存储区需容纳生成的目标代码与目标代码运行时的数据空间。
我们这里所说的运行时的存储区组织,是指目标程序运行时的数据空间的管理与组织。
【难重点】◇目标程序运行时,存储区域的整体布局,与各区域的作用。
◇各类不一致类型的数据表示。
◇同意过程嵌套定义的情况,栈式动态分配的组织管理。
◇对过程的调用,进入与退出时,栈式动态分配的工作原理。
◇过程活动纪录的各项内容与它们的作用,与活动纪录的组织方式。
◇过程参数传递的不一致方式。
【知识结构】从逻辑上看,在代码生成前,编译程序务必进行目标程序运行环境的配置与数据空间的分配。
通常来讲,假如编译程序从操作系统中得到一块存储区以使目标程序在其上运行,该存储区需容纳生成的目标代码与目标代码运行时的数据空间。
数据空间应包含:用户定义的各类类型的数据对象(变量与常数)所需的存储空间,作为保留中间结果与传递参数的临时工作单元,调用过程时所需的连接单元,与组织输入/输出所需的缓冲区。
目标代码所占用空间的大小在编译时能确定。
有些数据对象所占用的空间也能在编译时确定,其地址能够编译进目标代码中。
而有些数据对象具有可变体积与待分配性质,无法在编译时确定存储空间的位置。
因此运行时的存储区常常划分成:目标区、静态数据区、栈区与堆区,如图10.1就是一种典型划分,代码(code)区用以存放目标代码,这是固定长度的,即编译时能确定的;静态数据区(static data)用以存放编译时能确定所占用空间的数据;堆栈区(stack and heap)用于可变数据与管理过程活动的操纵信息。
编译原理-第十章--代码优化
第十章代码优化某些编译程序在中间代码或目标代码生成之后要对生成的代码进行优化。
所谓优化,实质上是对代码进行等价变换,使得变换后的代码运行结果与变换前代码运行结果相同,而运行速度加大或占用存储空间少,或两者都有。
优化可在编译的不同阶段进行,对同一阶段,涉及的程序范围也不同,在同一范围内,可进行多种优化。
一般,优化工作阶段可在中间代码生成之后和(或)目标代码生成之后进行。
中间代码的优化是对中间代码进行等价变换。
目标代码的优化是在目标代码生成之后进行的,因为生成的目标代码对应于具体的计算机,因此,这一类优化在很大程度上依赖于具体的机器,我们不做详细讨论。
另外依据优化所涉及的程序范围,又可分为局部优化、循环优化和全局优化三个不同的级别。
局部优化指的是在只有一个入口、一个出口的基本程序块上进行的优化。
循环优化对循环中的代码进行的优化。
全局优化是在整个程序范围内进行的优化。
本章重点:局部优化基本块的DAG表示第一节优化技术简介为了说明问题,我们来看下面这个例子,源程序是:P :=0For I :=1 to 20 doP :=P+A[I]*B[I];经过编译得到的中间代码如图10-1-1所示,这个程序段由B1和B2两个部分组成,B2是一个循环,假定机器按字节编址。
那么,对于这个中间代码段,可进行如下这些优化。
1、删除多余运算(删除公共子表达式)优化的目的在于使目标代码执行速度较快。
图10-1-1中间代码(3)和(6)中都有4*I的运算,而从(3)到(6)没有对I赋值,显然,两次计算机的值是相等的。
所以,(6)的运算是多余的。
我们可以把(6)变换成:T4 :=T1。
这种优化称为删除多余运算或称为删除公共子表达式。
2、代码外提减少循环中代码总数的一个重要办法是代码外提。
这种变换把循环不变运算,即其结果独立于循环执行次数的表达式,提到循环的前面。
使之只在循环外计算一次,上例中,我们可以把(4)和(7)提到循环外。
经过删除多余运算和代码外提后,代码变成图10-1-2。
编译原理-第十章习题答案
上一页
下一页
12
P:=0 for I:=1 to 20 do P:=P+A[I]*B[I]
(1)P:=0 (2)I:=1 (1)P:=0 (2)I:=1 (4)T2:=addr(A)-4 (7)T5:=addr(B)-4
(3)T1:=4*I (4)T2:=addr(A)-4 (5)T3:=T2[T1] (6)T4:=4*I (7)T5:=addr(B)-4 (8)T6:=T5[T4] (9)T7:=T3*T6 (10)P:=P+T7 (11)I:=I+1 (12)if I<=20 goto(3)
例: L1: if a<b goto L2 goto Lnext L2: if c<d goto L3 goto L4 L3: t1 =y+z x =t1 goto L1 L4:t2 = y-z x =t2 goto L1 L1:if a<b goto L2 L2:if c<d goto L3 goto L4 L3:t1 = y + z x = t1 goto L1
编译原理电子教案 第十章 优化
本章的主要内容
基本块的划分和流图的构建 基本块的DAG表示及基于DAG的局部优化 循环优化
上一页
下一页
2
本章要求
知识点:优化的基本概念及方法、基本块及程序流图、 DAG及基于DAG的优化、循环优化 熟练掌握: (1)局部优化:基本块,流图,DAG优化。 (2)循环优化:代码外提,强度削弱,删除归纳变量。
优化后: _tmp0 = 56 ; _tmp1 = _tmp0 – b ; a = _tmp1 ;
上一页 下一页
8
常数传播
_tmp4 = 0 ; f0 = _tmp4 ; _tmp5 = 1 ; f1 = _tmp5 ; _tmp6 = 2 ; i = _tmp6 ;
第10章 字符串数组(培训)
C语言程序设计
国家等级考试练习 1.有以下程序: main() {char ch[ ]="uvwxyz",*pc; pc=ch; printf("%c\n",*(pc+5)); printf("%c\n",*pc++); printf("%c\n",*pc++); } z 输出结果是: u v
C语言程序设计
C语言程序设计
10.2
使指针指向一个字符串
1 通过赋初值的方式使指针指向一个字符串 char *ps1="form one"; char str[]="form one",*ps2=str; 指针变量中保存的是字符串常量或字符串数组的首地址
C语言程序设计
2 通过赋值运算使指针指向一个字符串 char *ps1; ps1="form one"; char str[ ]="form two",*ps2; ps2=str; 3 用字符数组作为字符串和用指针指向字符串之间 的区别 char mark[ ]="program"; char *pmark[ ]="program";
C语言程序设计
4.以下程序运行后的输出结果是: bcdefgha #include <string.h> char *ss(char *s) { char *p,t; p=s+1; t=*s; while(*p) {*(p-1)=*p; p++; } *(p-1)=t; return s; } main() {char *p ,str[10]="abcdefgh"; p=ss(str); printf("%s\n",p); }
liu-第十章出错处理
(1) 静态语义错误 静态语义错误是在编译时发现的,大部分 错误是: 1)运算分量类型不匹配 例 A = B AND C 其中B,C定义为逻辑量,A定义为数值 量。
2)标识符没有定义就使用 例:begin real x; y:=x+2; y:=y*y; write(x,y) end 没有定义y变量的类型,编译时检查出3个 错误,这3个错误都是变量y引起的,这种 错误称重复性错误
(2)动态语义错误 动态语义错误是在运行时发现的源程序错误,这 种错误又称算法逻辑上的错误。最常见的动态语 义错误有下面几种情况: 1)除以零; 2)运算过程数值溢出; 3)动态数组下标值越界; 4)变量没有赋初值; 5)指针变量为NIL; 6)运行结果与程序设计结果不一致等。 前五种情况往往导致程序非正常停止运行得不到 结果,而第六种情况程序正常停止运行,但得不 到正确结果,属于程序算法上的错误。
拼写错误处理的主要思想: (1)从符号表中选出一个子集,使得子集包 含所有那些可能拼错的标识符; (2)检查子集中的每个标识符号,看它是哪 类拼写错误,再进行校正。 注意:若当前拼错的标识符是保留字,则 应从保留字表中选此子集,而不是到标识 符表中选子集。
2.语法错误 语法错误指程序书写不符合语法规则要求, 是在语法分析阶段出现的。例如,条件语 句IF缺少ELSE或THEN;保留字、变量没 有说明定义;语句之间缺少换行符号;括 号不匹配等。
3.语义错误 语义错误指源程序结构不符合语义规则或 违反了具体程序设计语言的限制而产生的 错误。 语义错误的主要表现:源程序错用了标识 符及表达式。
语义错误分静态语义错误和动态语义错误。 静态语义错误是在编译时发现的错误,例 如,数据类型不匹配。 动态语义错误是在目标代码运行时发现的 错误,这一类错误,一般称算法逻辑错误, 例如,计算, x0;下界越界;变量没 赋初值等。
编译原理_第三版_课后答案
1 0 a b (b) b 0 3 2 a a a b 5 4 1 a 已经确定化了,进行最小化 最小化: b 0 2 1 a a b b a a b a b b a
P64–14
(1) 1 0 0 1
0 (2):
Y X 2 0 1 Y 1 X 0 确定化: 0 {X,1,Y} {1,Y} {2} φ 给状态编号: 0 1 2 3 0 1 0 {1,Y} {1,Y} {1,Y} φ 0 1 1 1 3 1 {2} {2} φ φ 1 2 2 3 3
P36-10
/************** ***************/
P36-11
/*************** L1: L2: L3: L4: ***************/
第三章习题参考答案 P64–7
(1)
X Y 0 X 1 2 3 4 Y 5 1 1 1 确定化: 0 {X} φ {1,2,3} {2,3} {2,3,4} {2,3,5} {2,3,4,Y} φ φ {2,3} {2,3} {2,3,5} {2,3} {2,3,5} 0 1 3 2 0 1 {1,2,3} φ {2,3,4} {2,3,4} {2,3,4} {2,3,4,Y} {2,3,4,} 1 0
动
进 进 归
进
归
P134–5
(1)
0. 4. 8. (2) 1
1. 5. 9. 6. 10.
2. 11.
3. 7.
S 9 8 7 S a 11 10 0
A
4 3 2 A d 5 6 确定化: S {0,2,5,7,10} {1,2,5,7,8,10} {2,3,5,7,10} {2,5,7,8,10} {1,2,5,7,8,10} {2,5,7,8,10} {2,4,5,7,8,10} {2,5,7,8,10} A {2,3,5,7,10} {2,3,5,7,9,10} {2,3,5,7,10} {2,3,5,7,9,10} a {11} {11} {11} {11} b {6} {6} {6} {6} S
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
10.3 栈式存储管理
一、允许过程(函数)递归调用的数据存储管理 1、语言特点 允许过程(函数)的递归调用,但不允许定义嵌套的 过程(函数),也不许使用可变数组。如C语言。 2、栈式存储分配 每进入一个过程,就有相应的数据区建立在栈顶。 当程序开始运行前,用于建造数据区的栈是空栈。当 开始进入主程序执行语句前,便在栈中建立全局变量 和主程序数据区。在主程序中若有调用过程的语句时 ,便在栈顶累筑该过程的活动记录。在进入过程后执 行过程的可执行语句前再把局部数组累筑于栈顶,若 还有调用过程的语句就重复上述过程。
C语言的活动记录
TOP
临时工作单元
内情向量
x
简单变量 形式单元 形参个数 返回地址
2
1
0
老SP
注: 1)活动记录的建立是按照调用次序而累筑,而非排 列次序; 2)栈顶活动记录数据区有两个指针SP和TOP,SP 指向现行数据区起点,TOP指向顶点; 3)从数据区中引出指向主程序数据区的箭头表示外 部变量引用关系;
3. 参数个数和形式单元 4. DISPAY表。 5. 过程所辖的各分程序的局部数据单元。 对于每个分程序来说,它们包括: (1)分程序的TOP值。当进入分程序时它 含现行栈顶地址,以后,用来定义栈的 新高度(分程序的TOP值); (2)分程序的局部变量, 数组内情向量和 临时工作单元。
Procedure A(m,n); integer m,n; B1:begin real z; array B[m:n]; B2:begin real d, e; L3: end; B4:begin array C[1:m]; B5:begin real e; L6: 5 end; end; L8:end;
2 1 4
分程序结构的存储分配方案
• 处理分程序结构存储分配方案的一种简单办法 是,把分程序看成 “无名无参过 程”,它在哪 里定义就在哪里被调用。因此,可以把处理过程 的存储办法应用到处理分程序中。但这种做法是 极为低效的。 • 一则,每逢进入 一个分程序,就照样建立连接 数据和DISPLAY表,这是不必要的。 二则 ,当 从内层分程序向外层转移时,可能同时要结束若 干个分程序。
4)C语言的活动记录所含区段是:连接数据(包含老 SP值(即前一活动记录的首地址;或称施调过程的 数据区首地址)和返回地址(即调用语句的下一条指 令的地址))、参数(形参)个数、形式单元(存放实参 值或地址)、过程的局部变量(简单变量)、数组内情 变量和临时工作单元。 5)过程中的局部变量(简单变量)在内存地址可表示 为变址形式x[SP],其中SP为当前数据区首地址, 用作变址值,x称为相对位移量。
TOP----> 临时工作单元 局部简单变量 TOP R的数组区
局部数组的内情向量
保存运 行过程前的状态 (返回地址,寄存器值……) SP
R的活动记录
Q的活动记录 主程序全局
实参(形式单元)和参数个数
SP-----> 控制链(老SP) 无嵌套定义的过程活动记录内容
数据区
分配了数组区之后的运行栈
二、嵌套过程语言的栈式分配方案
注:3)若使用栈式存储管理策略,则运行时 ,每进入一个过程,就在栈顶为该过程分 配一块数据区,一旦退出该过程,它所占 的空间也退还给系统。 4)有些程序设计语言允许用户随时动态申请 和释放存储空间,而且申请和释放之间不 遵循先申请后释放或后申请先释放原则, 此时需要使用堆式存储管理。 5)若使用堆式存储管理策略,则让运行程序 持有一个大的存区(堆),在申请时从堆中取 一块,释放时将一块存储区退还给堆。
10.3 参数传递
• 传值(值调用) 特点是对形式参数的任何运算不影响实参的值。 例如:过程 swap(x,y:integer); swap(a,b);其结果: a,b调用前的值不改 变。 • 传地址(变量参数) 例如:过程 swap(var x,y:integer); swap(a,b);( a,b为调用时的实参 ) 调用结果a,b的值被改变。
Global Static Local dynamic
10.2 数据空间的三种不同使用方法和管理 1、静态存储分配 在编译时就可以完全为数据项目分配 存储单元,称为静态存储分配。 注:若一个程序设计语言不允许递归 调用,而且不含有可变数组,则可使用 静态存储分配策略。
2、动态存储分配 在运行时才能进行数据存储单元分配,称 为动态存储分配。 注:1)若某程序设计语言允许过程递归调用 ,而且允许使用可变数组,那么在编译时就 不可能完全为其数据项目分配存储单元,必 须采取动态存储分配策略。 2)动态分配数据单元时一般使用栈,即,栈 式存储管理。
(2)
• 主程序--->P--->Q--->R
R 的 display sp 活动记录 d[1] Q 的 d[0] 活动记录 P 的 活动记录 主程序的 活动记录
(4) top
top Q display sp d[2]
d[1] d[0]
的 活动记录 P 的 活动记录 主程序的 活动记录
(3)
三、分程序结构
例如有如下程序: MAIN () {…… Q( ); } P( ) {……} Q( ) { …… P( ); …… }
当P过程进入运行 后,栈顶数据区 有两个指针:
– SP指向现行过 程数据区起点; – TOP指向顶点
TOP
P过程数据区
SP
从数据区中引出指 向主程序数据区 的箭头表示外部 变量引用关系。
• 按照过程处理办法,意味着必须一层一层 地通过“返回” 来恢复所要到达的那个分 程序的数据区,但不能直接到达。 例如:如果有一个从第5层分程序转出到达 第1层分程序的标号L,虽然在第5层分程 序工作时知道L所属的层数,我们极易从 DISPLAY中获得第1层分程序的活动记录 基址(SP),但是怎么知道第1层分程序 进入时的TOP呢?唯一的办法是从 5,4,3 和2各层顺序退出。但这种办法是很浪费时 间的。
2 1 4
K D 6 5 4 3 2 1 0
变 量e 变 量e和d B5 的 T O P 数 组C的 内 情 向 量 B4 的 T O P B2 的 TOP 数 组B的 内 情 向 量 变 量 z B1 的 T O P DISPLAY 形 式 单 元 m,n 参 数 个 数:2 调 用 时 的 栈 顶 地 址(老 T O P) 全 局DISPLAY 地 址 返 回 地 址 老SP 过 程 的 T O P,指 向 活 动 记 录 之 顶
目标代码区
静态数据区 Stack
heap
运行环境和存储分配设计分析
逻辑阶段:在目标代码生成前,作准备 实质: 关联(Binding)
将源程序的文本 源文件中的名字N 程序运行动作的实现 运行时的存储S
决定运行管理复杂程度的因素——源语言本身 1. 允许的数据类型的多少 2.语言中允许的数据项是 静态确定 动态确定 3.程序结构 决定名字的作用域的规则和结构 A.段结构 B.过程定义不嵌套,只允许过程递归调用 C.分程序结构 分程序嵌套 过程定义嵌套 4存储类别的多少
程序结构图
R 主
P
Q
call R
call Q call P call R
用Display表的方案
(1)主程序--->(2)P--->(3)Q--->(4)R
top
top display d[0]
主程序的 sp 活动记录
display d[1]
d[0]
(1)
P 的 sp 活动记录 主程序的 活动记录
每个过程被当作是0层分程序。而过程体 分程序(假定是一个分程序)当作是它 : 所管辖的第1层分程序。 这样,每个过程的活动记录所含的内容有: 1.过程的TOP值,它指向过程活动记录的 栈顶位置。 2.连接数据,共四项: (1)老SP值; (2)返回地址; (3)全局DISPAY地址; (4)调用时的栈顶单元地址,老TOP。
为了解决上述问题,可采取两种措施。 • 第一,对每个过程或分程序都建立有自 己的栈顶指示器TOP,代替原来仅有过 程的栈顶指示器, 每个TOP的值保存在各 自活动记录中。这样,上述的第二个问 题便可解决。 • 第二,不把分程序看作“无参过程”, 每个分程序享用包围它的那个最近过程 的DISPLAY。每个分程序都隶属于某个 确定的过程,分程序的层次是相对于它 所属的那个过程进行编号的。
10.1
概述
• 代码生成前如何安排目标程序运行环境 的设计和数据空间的分配。 • 数据的存储单元 变量、常量单元,临时工作单元,返 回地址,缓冲区。 注:编译程序必须为目标程序的运行分配 数据的存储单元,若无存放数据信息的 单元,则目标程序将无法运行。
通常存储区布局可为:目标区, 静态数据区,栈区和堆区
第十章 目标程序运行时的存储组织
1、理解基本概念:静态存储分配,动 态存储分配,栈式存储分配,堆式存 储分配,活动记录; 2、理解运行阶段存储组织的基本思想与 分配方案, 栈式存储分配的实现, 参数传递中值传递、地址传递方式;
目录
10.1 概述 10.2 数据空间的三种不同使用方法 和管理方法 10.3 参数传递
1. 用控制链
• 在过程活动记录中增设存取链,指向包 含该过程的直接外层过程的最新活动记 录的起始位置。见P223
2、用Display表 Display表---嵌套层次显示表 当前激活过程的层次为K,它的Display 表含有K+1个单元,依次存放着现行 层,直接外层…直至最外层的每一过程 的最新活动记录的基地址。
Q过程数据区
主程序数据 区
C数据区结构
3、 活动记录AR(Activation Record):一个过程 的一次执行所需要的信息使用一个连续的存储区 来管理,这个区(块)叫做一个活动记录。
一般活动记录要记录: l 临时值,如计算表达式时的中间工作单元。 l 局部变量 (数据) l 保存运行过程前的状态 (返回地址,寄存器值……) l 存取链(可选) 对于非局部量的引用。 l 控制链(可选) 指向调用者的活动记录,释放栈。 l 实参(形式单元) l 返回值(对函数) (有时可使用寄存器存放返回值)