名 一个名字的属性包括类型和作用域。名字的类型决定了它能具有什么样的值,值在 计算机内的表示方式,以及对它能施加什么运算。名字的作用域规定了它的值的存在范 围。例如,一个Pl名的作用域是那个包含此名的说明的过程,只有当这个过程运行时 此名字才有对应的存储单元。 在多数的程序语言中,名字的性质是用说明句明确规定的。例如在P©l中,说明句 X.Y:real; 规定了名字X、Y代表实型(简单)变量。即,X和Y各对应有一个标准长度的存储单元, 其内容是浮点数,可对它们进行各种算术运算。 在某些语言中,名字的性质有时容许是隐约定的。例如在FORTRAN中,对未经说明 句明显说明的名字,凡以1,J,.,N为首者均认为是代表整型的,否则为实型的。 某些语言既没有说明句也没有隐约定,如APL就是这样。在这种语言中,同一标识 符在某一行中可能代表一个整型变量,而在另一行中则代表一个实型数组,因此,名字的 性质只能在程序运行时“动态”地确定。也就是,“走到哪里,是什么,算什么”。 如果一个名字的性质是通过说明句或隐约定规则而定义的,则称这种性质是“静态” 确定的。如果名字的性质只有在程序运行时才能知道,则称这种性质是“动态”确定的(我 们以后常用“静态”和“动态”两词。凡编译时可以确定的东西称为“静态”的:凡必须推迟 到程序运行时才能确定的东西称为“动态”的)。 对于具有静态性质的名字,编译时应对它们引用的合法性进行检查。例如,假定【是 整型变量,X是实型变量,混合加法运算I+X在FORTRAN66中是不允许的,而在Pascal 中则是认可的,但必须预先产生把【转换成实型量的代码。 对于具有动态性质的名字,应在程序运行时收集和确定它们的性质,并进行必要的类 型转换。名字的动态性质对于用户来说是方便的,但对计算机实现来说则其效率较低。 二、数据结构 许多程序语言提供了一种可从初级数据定义复杂(高级)数据的手段。下面我们将概 述几种常见的定义方式。 1.数组 从逻辑上说,一个数组是由同一类型数据所组成的某种维矩形结构。沿者每一维 的距离称为一个下标。 每维的下标只能在该维的上、下限之内变动。数组的每个元素是 矩形结构中的一个点,它的位置可通过给出每维的下标来确定。 数组的每个元素(也称下标变量)是由数组名连同各维的下标值命名的,如A[, 2,“,]。根据数组的类型,每个数组元素在计算机中占有同样大小的存储空间。如果 个数组所需的存储空间大小在编译时就已知道,则称此数组是一个确定数组;否则,称 为可变数组 数组的存绪表示有多种形式,最简单的一种是把整个数组按行(或按列)存放在一片 连续存储区中。一般而言,假定对一个n维数组附上一个n位数码管显示器,每个管代表 个下标,每管显示的值在相应维的下限与上限之间变动。所谓按行存放意味着,当从数 组的第一个元素开始扫描整个数组时,越是后面的下标(数码管)变化得越快。按列存效 意味着越是前面的下标变化得越快。 有些程序语言,如FORTRAN,.婴求以列为序存放数组;另一些,如Pascal,通常以行为
序:还有一些则取决于编译程序设计者的意愿。 数组元素的地址计算和数组的存储方式密切相关。关于数组元素的地址计算公式, 数据结构教材中有详细的介绍。编译程序要做的工作就是实现地址计算公式,使数组元 素得到正确引用。 在编译过程中,当碰到数组说明时,必须把数组的有关信息记录在一个“内情向量”之 中,以便以后计算数组元素的地址时引用这些信息。每个数组的内情向量必须包括:维 数,各维的上、下限,首地址,以及数组(元素)的类型,等等。 对于确定数组来说,内情向量可登记在编译时的符号表中。对于可变数组,内情向 的一部(或全部)在编译时无法知道,只有在程序运行时才能计算出来。因此,编译程序必 须为可变数组设置一定的空间,以便在运行时建立相应的内情向量。不论是对确定数组 或可变数组,数组元素的地址计算公式都是一样的。 2.记录 从逻辑上说,记录结构是由已知类型的数据组合起来的一种结构。例如下面登记(姓 名、年龄、婚否)的卡片就是一类简单的记录结构 NAME LI MING AGE 25 MARRIED NO 每个这种记录结构含有三个分量:第一个分量NAME是个字符串型的数据(或一个 字符数组):第二个分量AGE是一个整型数拐:第三个分量MARRIED是一个逻辑型数据。 一个记录结构通#含有若干个分量,每个分量称为记录的一个栏(或域d)。每个 分量都是一个确定类型的数据,不同分量的数据类型可以不同。 记录结构是许多程序语言中的一类重要的数据结构。不同语言定义记录结构的方式 也有不同。例如,Pa©l语言采用下面形式定义记录: CARD:record NAME:array [1.20]of char; AGE:integer: MARRIED:boolean end: 这个说明句定义了一个记录CARD。它是一个含有三个分量的记录结构:NAME,字符数 组;AGE,整型量;MARRIED,布尔量 当需要了解或更改某一卡片(如CRD)的某一栏信息时,一般可采用如下的复合名 进行访问:CARD.NAME,CARD.AGE和CARD.MARRIED。例如,可使用下面三个语句来 填写卡片: CARD.NAME:='LI MING'; CARD.AGE:=25: CARD.MARRIED:=false 记录结构最简单的存储表示方式是连续存放。以上述的CARD为例,假定:目标机器
22 按字节编址,每个字节存放一一个字符:每个机器字包含四个字节,可存放一个整数,整数单 元必须从字的边界开始(即地址码为4的倍数):每个布尔量用-字节表示 那么,每张卡 片(即CARD记录)需用25B。由于整型量ACE必须从字的边界开始,因此,每张卡片最好 用28个字节,也就是7个字:NAME占5个字,AGE占1个字,MARRIED占1个字(浪费三 个字节的零头)。这样一来1000张卡就需要7000个字。 记录结构的每个分量(域)所需占用的存储单元(字节)数称为该域的长度。当知道 个记录的地址后,通过每个域的长度就可算出各域的地址。因为我们容易推出每个城相 对于记录结构起点的相对数OFFSET:此域之前各域长度的总和。例如,就CRD而言 AME、AGE和MARRIED的相对数OFFSET分别为0,20和24。于是,假定CARD的首地 址为a,那么。 CARD·NAME 的地址为 a CARD·AGE 的地址为 盘+20 CARDMARRIED 的地址为 a+24 3.字符串、表格、栈和队列 某些语言(如SNOBOL)把字符串作为一种基本数据类型,申的长度也不加限制。这 种数据类型对于符号处理,公式处理是完全必须的。 有些语言(如SP)特别适用于描述表格处理,因此表格就成为一种十分重要的数据 类型。一个表格本质上是一组记录结构,它的每一栏可以是初等类型数据,也可以是一个 指向别的记录结构的指示器。所谓线性表是指-一组顺序化的记录结构。 有些语言提供了某种简易的手段,它使程序员可以方便地定义各式各样的栈和队列 有些语言,如Pas©l虽没有明显地提供栈型的数据结构,但栈却是它的程序数据空间的基 本组织形式。 三、抽象数据类型 为了增加程序的可读性和可理解性,提高可维护性,降低软件设计的复杂性,许多的 程序设计语言提供了对抽象数据类型的支持。一个抽象数据类型包括: (1)数据对象的一个集合: (2)作用于这些数据对象的抽象运算的集合: (3)这种类型对象的封装,即,除了使用类型中所定义的运算外,用户不能对这些对 象进行操作。 在常用的程序设计语言中,Ada语言通过程序包(Package)提供了数据封装的支持, 业、C++和Java语言则通过类(Ca)对抽象数据类型提供支持。 2.2.4语句与控制结构 除了提供数据的表示、构造及运算设施外,程序设计语言应该有可执行的语句。控制 结构定义了语句在其中的执行次序,语言所提供的控制结构的集合对可读和可维护的软 件的线写有很大的影响。 、表达式 一个表达式是由运算量(亦称操作数,即数据引用或函数调用)和算符组成的。例如 算术表达式X+Y是由二元(二目)算符·+'和运算量X、Y(数值数据)组成的。X和Y分
23 别称为算符+的左、右运算量(左、右操作数) 在表达式中,-一元算符通常写在它的运算量的前面,如-X和B,这种形式称为前缀 形:但有些也写在运算量后面,如P个(P为指示器),这种形式称为后缀形。 在许多语言 中,符号‘-'既用来表示一元算符“负”(如-X),又用来表示二元算符“减”(如X-Y)。 在多数程序语言中,二元算符一般都写在两个运算量中间,如X+Y,这种形式称为中 缀形式。但有的也采用后缀形式,即把靠符写在运算量的后边,如把X+Y写成XY+。 理论上说还有一种前缀形式,即把算符写在前面,把运算量写在后面,如把X+Y写成 +XY 对于多数程序语言来说,表达式的形成规则可概括为 (1)变量(包括下标变量)、常数是表达式: (2)若E、2为表达式,8是一个二元算符,则E郎2是表达式(一般采用中缀形式); (3)若E是表达式,0为一元算符,则E(或)是表达式: (4)若E是表达式,则(E)是表达式。 表达式中算符的运算顺序和结合性的约定大多和通常的数学习惯相一致。例如,对 于算术表达式的计值过程一般都遵循:先乘除后加减,乘幕更优先。对于同级算符,优先 的规则视具体情形而定,可采用先左后右(左结合)或先右后左(右结合)的运算顺序。例 X+#7 等于X+(YZ) 优先于+ X-Y-Z 等于(X-Y)-Z 同级优先左结合 X-Y+Z 等于(X-Y)+Z 同级优先左结合 等于X*(Y*Z) 司级优先右结合 多数语言中,算术算符和逻辑算符的优先顺序一般规定如下(自高至低排列,同级算 符列于同一行): 乘释 (¥或个) 一元负 (-) 乘、除 (,/,±) 加、诚 (+,-) 关系符 (<,=,>,<=,<>,>=) 非 (a,not成.NOT.) 与 ( ,&,ad或AND-) 或 (V,I,r或0R~) 隐含 (。或imo) 等值 FORTRAN关于算符优先级的规定和上面所列的基本一致。事实上,不同的语言对算 符优先级和结合性质的规定各有差异,有的甚至差异很大。例如,APL规定所有算符都具 有相同的优先级并 一律服从右结合,于是,X-Y+Z就意味着X-(Y+Z)。ALC0L对于 同级优先的算符要求严格服从自左至右运算规则,即左结合。例如X+Y+Z必须处理成 (X+Y)+Z。FORTRAN对于满足左、右结合的算符可任取一种结合。例如,A+B+C可 处理成(A+B)+C,也可处理成A+(B+C);对于满足交换律的算符,左,右运算量的计算
2 顺序也不加限制,例如A*B+C*D也可处理成CD+B¥A。 算符的代数性质(交换律、结合律和分配律)常常可用来优化目标程序的质量。但必 须注意两点:第一,代数性质能引用到什么程度视具体语言的不同而不同。例如,在L COL中,若把A*B+C*D处理成C*D+B*A,则至少是对AGOL不够忠实。第二,在 数学上成立的代数性质在计算机上未必完全成立。交换律在计算机上一般是成立的,但 结合律和分配律就未必成立(至少在结果的有效数位上常有差别)。例如,在计算机上」 (A+B)+C=A+(B+C)并不普遍成立。因此,在某些语言(如FORTRAN)中,为了保证运 算结果的有效性,程序员应尽量用括号来组织表达式的计值顺序。 某些语言中容许对不同类型的数据进行运算,有些则禁止。例如,0.5+3是一个正 确的AGOL表达式,但在标准FORTRAN(66)中是不能容忍的。如果容许对不同类型的 数据进行运算,那就必须规定运算结果的性质,使得在编译时能够预先产生对运算量进行 类型转换的目标代码。 二、语句 不同程序语言含有不同形式和功能的各种语句。从功能上说,语句大体可分执行性 语句和说明性语句两大类。说明性语句旨在定义各种不同数据类型的变量或运算。执行 性语句旨在描述程序的动作。执行语句又可分赋值句、控制句和输入输出句。从形式上 说,语句还可分为简单句、复合句和分程序等等。 1.成值句 不同语言的赋值句有不同的语法结构,但多数语言所定义的语义大体相同。我们考 虑下面一个AGOL赋值句 A:=B 其中,A,B为变量名。我们知道,每个名字有两方面的特征:一方面它代表一定的存储单 元,另一方面它又以该单元的内容作为值。赋值句A:=B的意义是:“把B的值送人A所 代表的单元”。也就是说,在赋值句中,赋值号·:='左、右两边的变量名扮演着两种不同 的角色。对赋值号右边的B我们需要的是它的值:对左边的A我们需要的是它所代表的 那个存储单元(的地址)。为了区分一个名字的这两种特征,我们把一个名字所代表的那 个单元(地址)称为该名的左值;把一个名字的值称为该名的右值。直观上说,名字的左值 指它所代表的存储单元的地址,右值指该单元的内容。 变量(包括下标变量)既持有左值又持有右值。常数和带有算符的表达式一般认为只 持有右值。但对于指示器变量,如P,它的右值P个既持有左值又持有右值。出现在赋值 号左边的表达式必须持有左值。出现在赋值号右边的表达式只需持有右值。对于在赋值 号右边的表达式中出现的任何变量,我们要的是它的右值。但队S3的情形甚为特别,在 那里任何单独出现的名字均要其左值,欲使它代表右值,则需在名字之前加一圆点。因 此,在B旺ISS中,A一B+4和A一B+4代表两个不同意义的赋值句。 2.控制语句 许多语言具有形式上很不相同的控制语句(控制程序的执行顺序),有的即使形式上 相同,语义上也可能有很大差别。多数语言中所含的控制语句有: 无条件转移语句 goto L