PASCAL作为一种结构化程序设计语言,要求编程者尽量将程序分成子程序(程序块),甚至在子程序中再嵌套子程序。这就使得程序中使用的标识符存在一个作用域问题,当主程序和子程序中存在同名标识符时,它们的作用域很容易混淆,而在多人合作编制一个程序或者在不同的子程序中使用常用变量时容易出现同名标识符的情况。本文就此详细讨论一下PASCAL中的作用域问题。
例1:
PROGRAM scopel(input, output);
CONST
a='scope';
VAR
i,j:integer;
x:real;
FUNCTION e(x:integer):real;
VAR
i:char;
BEGIN
…
END;{of e}
PROCEDURE f;
VAR
i,k:real;
x:integer;
PROCEDURE e;
VAR
x:char;
BEGIN
…
END;{of e}
BEGIN
…
END;{off}
BEGIN
…
END.{of scopel}
例2:
PROGRAM scope2(output);
CONST name='scope';
VAR c:char;
PROCEDURE a;
CONST name='A';
VAR c:char;
BEGIN
c:='C';
writeln(name,c);
END;{of a}
BEGIN
C:='B';
writeln(name,c);
a;
writeln(name,c);
END.{of scope2}
1,标识符定义与使用的规则
在PASCAL中,标识符的定义与使用必须遵循以下
两个规则:
①标识符必须先定义后使用。
②在同一个程序块中,一个标识符只能被定义一
次,而且只能用于定义它的程序块及该程序块包
括的程序块中。
一个程序块是指由块头、说明部分、语句部分及
块尾组成的整体。如例1中,PROGRAM scopel、
FUNCTION e、PROCEDURE f及PROCEDURE e都是
程序块,它们之间的关系如图1所示,PROGRAM
scopel包含FUNCTION e和PROCEDURE f, PROCEDURE f
包含PROCEDURE e, 而FUNCTION e和PROCEDURE f是
并列关系。
为了便于下面的解释,我们在程序左边标以行号,
用“行号+标识符”的格式来区别不同的标识符,
如(6)x与(7)x分别表示第6行及第7行的x等等。
2几个概念
同名标识符
在某个程序块中定义的标识符,如果在别的程序
块中重新被定义,则该标识符在新程序块中有了
新含义,与原来的标识符无关。但这两个标识符
名称相同,所以又称为同名标识符。如例1中(6)
x在scope1中被定义,又在(7)e、(13)f和(17)e中
分别被重新定义,所以(6)x、(7)x、(16)x和(19)
x是同名标识符。同样,(5)i、(9)i和(15)i是同
名标识符,(7)e和(17)e是同名标识符。
全程量
在主程序中定义、主程序以及该主程序的所有子
程序都能对其进行访问的标识符。如例1中的
(3)a、(5)i、(5)j、(6)x、(7)e、(13)f都是全程量。
局部量
在最内层子程序中定义,只有该子程序能对其进行
访问的标识符。如例1中的(7)x、(9)i、(19)x。
非局部量
在外层子程序中定义,该子程序及嵌套在该子程
序中的其它子程序都可以对其进行访问的标识符。
如例1中的(15)i、(15)k、(16)x、(17)e。
从物理意义上讲,全程量在程序的整个运行期间
都占据着存储单元,而局部量和非局部量仅在程序
运行到有关子程序时才被分配所需存储单元,一
旦该子程序执行结束,其所分配的单元即被释放。
因此,即使全程量同局部量、非局部量同名,也
被分配不同的单元,它们是完全不同的量,对其
中任一量的赋值不会改变其它同名量的值。
作用域
一个标识符能被访问的程序范围就称作该标识
符的作用域。如例1中(3)a、(5)j、(13)f的作用
域是S1,(7)x、(9)i的作用域是S2,(19)x的作
用域是S4,(15)i、(15)k、(17)e的作用域是S3
。但是(6)x情况不同,由于S1中出现了它的同名
标识符(7)x、(16)x及(19)x,在S2及S3中,(6)x
分别被(7)x及(16)x屏蔽,所以(6)x的作用域为
S1-S2-S3。
标识符的作用域规则 局部标识符的作用域就是
定义该局部标识符的程序块大小,其它标识符的
作用域是对该标识符定义的程序块减去内部同名
标识符的作用域所剩下的部分。如例1中(16)x的
作用域为S3-S4,(5)i的作用域是S1-S2-S3,
(7)e的作用域是S1-S3。
下面再看一下例2,它的执行结果是:
scopeB
AC
scopeB
3作用域在程序设计中的运用
(1)除标准标识符可以直接使用以外,自定义标
识符必须先定义后使用,而且同一程序块中只
能定义一次。
(2)内层和外层程序块都要使用的量,其定义必
须放在外层程序块中。
(3)只在内层程序块中使用的量,最好放在本层
而不是外层程序块中。
(4)并列的程序块可以使用同名标识符。
(5)相嵌套的程序块可以使用同名标识符。
4作用域在读程序中的运用
(1)在主程序中遇到的标识符一定是全程标识符
,因为同名的局部量和非局部量在说明它们的程
序块之外没有意义。
(2)在子程序中遇到的标识符,如果不是同名标
识符,则由定义该标识符的程序块来决定是全程
量、非局部量还是局部量;如果是同名标识符,
则一定是非局部量屏蔽全程量,局部量屏蔽非
局部量。
5程序设计中应注意的问题
(1)在子程序中尽量少用非局部量和全程量,否则
可能产生意想不到的错误。如例3:
PROGRAM scope3(output);
VAR x:integer;
FUNCTION a:integer;
BEGIN
x:=x+1;
a:=100;
END:{of a}
BEGIN
x:=1;
…
END.
这时表达式x+a和a+x的结果将不相同,这就违背
了加法交换律。
(2)如果在子程序中使用了同名变量,并且该局部变
量忘了在子程序中定义,那么编译很可能通过,但是
程序结果不对,而且很难找出错误。