pascal语言中指针类型和动态数据结构
整型、实型、布尔型等各种简单类型和数组、记录、集合等各种结构类型的数据都属于静态类型的数据。所谓静态类型数据是指使用前必须在程序的说明部分给出描述这种数据的类型说明(TYPE语句)或变量说明(VAR语句),以定义这类数据占用内存空间的大小规模,使系统在程序的编译阶段能对这些变量进行内存空间的分配,空间一旦分配则不能在程序的执行过程中加以改变。现在讨论另一种类型的数据,这些数据和静态类型数据不同,它们无需在程序的变量说明部分对其进行说明,也就是系统在程序编译阶段不对这些变量分配内存空间,而是在程序的运行过程中根据需要用相应的命令动态地建立、分配内存空间,以至这种类型的数据占用内存空间的大小规模可以动态地发生变化,故称之为动态类型数据结构。
一、指针变量以及动态数据的产生
讨论动态类型数据是如何在程序的运行阶段动态地建立起来的,就要讨论与之有关的一种静态类型数据 指针。
指针类型属于静态的简单类型,但和整型、实型、字符型这些简单类型不同。首先整型、实型、字符型的变量单元中存放的是相应类型的数据,而指针类型变量单元中存放的是某种类型变量单元的地址,通过该地址可以找到这种类型的数据,所以称它是一个指针,而这种数据就是动态数据;其次整型、实型、字符型等类型都有规定的标准标识符integer、real、char等与之对应,而指针类型则没有相应的标准标识符。这是因为在动态数据产生的过程中,程序员关心的是指针指向一个什么类型的数据。所以,在Pascal程序说明部分定义指针类型时必须给出该指针类型变量所指向的数据类型,即该指针类型标识何种类型的变量。
如在程序的类型说明部分有:
TYPE
pint=^integer;
pre=^real;
表明程序定义了两种指针类型pint和pre。pint用于标识整型的变量,pre用于标识实型的变量。
若在程序的变量说明部分有指针类型变量的说明:
VAR p1,p2:pint;
q:pre
表明程序定义了三个指针变量,p1和p2是pint类型的变量,q是pre类型的变量。在程序的编译阶段,同其它简单变量(整型、实型、字符型等)一样,系统要给它们分配空间,这就是前面所说的指针变量是静态变量。在内存空间分配之初,p1、p2和q变量单元还未定义,此时指针变量不指向任何一个内存变量单元,因为所需指向的动态数据单元还未产生,它们是在程序运行过程中利用p1、p2和q变量以及new过程语句动态地产生的。一旦有动态数据产生,则p1、p2变量单元中将存放动态产生的整型变量单元的地址,q变量单元中将存放动态产生的实型变量单元的地址。
动态数据的产生是系统在程序运行阶段执行过程语句new(<指针变量名>)时进行,所产生的动态数据单元用:“<指针变量名>^” 来命名:
new(p1):系统动态地产生整型变量单元,即动态地给一个整型变量分配内存单元,并将该单元的地址放入p1单元中。该整型变量单元命名为p1^,表明通过p1指针可访问这个单元的整型数据。
new(q):系统动态地产生实型变量单元,即动态地给一个实型变量分配内存单元,并将该单元地址放入q单元中。该实型变量单元命名为q ^,表明通过q指针可访问这个单元的实型数据。
二、指针类型变量的应用以及动态数据的操作
下面讨论指针类型变量的应用,即如何将整型数据或实型数据等存放到动态产生的相应的内存变量单元中,并对这些单元进行必要的操作。
首先可以用赋值语句将一个指针变量的地址值赋给另一个指针变量,如:
p2:=p1,表示将p1单元的地址值赋给p2,此时p2单元的内容也是指向某整型变量单元的地址值。
但p1:=q不行,因为在定义中可见两指针类型所标识的类型变量不同。
下面通过一个简单的例子看指针类型变量的应用:
PROGRAM EX00(OUTPUT);
TYPE
pint=^integer;
pre=^real;
VAR p:pint;
q:pre;
BEGIN
NEW(p);
p^ :=3;
NEW(q);
q^ :=4.5;
q^:=q^* p^;
WRITELN(q^);
DISPOSE(p);DISPOSE(q)
END.
最后语句DISPOSE(p)以及DISPOSE(q)为释放由p和q指针所指向的变量单元。
指针类型变量所指向的数据类型可以是整型,实型,字符型等简单类型,也可以是数组、集合、记录等结构类型。推而广之就有了链表、树结构等的产生及其应用。
如在程序说明部分有链表定义:
TYPE
Lp=^Litem
Litem=RECORD
int:integer;
next:Lp
END;
在该定义中,定义了指针类型Lp,用于标识Litem记录类型的变量。但我们注意到,在定义指针类型Lp时,Litem尚未定义,它的定义是在Lp的定义之后给出的。这种先使用后定义的情况在以上的链表定义中是允许的。
若在程序变量说明部分有:
VAR
L:Lp
则变量L 为一指针类型变量。在程序运行中,它的值将是一动态产生的记录型变量单元的首地址,即L 是指向记录型数据的指针变量。
若在程序中有:
new(L):系统动态地产生记录型变量单元,即动态地给一个记录型变量分配内存单元,并将所分单元的首地址放入L单元中。分配的记录型变量命名为L^,该记录型变量L^有两个域,一是整型数据域L^.int,一是指向另一记录型变量的指针域L^.next。
new(L^.next):同上,系统动态地给一个记录型变量分配内存单元,并将所分单元的首地址放入L^.next单元中。分配的记录型变量命名为L^.next^,该记录型变量L^.next^ 同样有两个域,一是整型数据域L^.next^.int,一是指向另一记录型变量的指针域L^.next^.next。
接下来还可有new(L^.next^.next),new(L^.next^.next^.next)……,一个链表数据结构就随之产生了。但因为链表是一个动态数据结构,它的长度,即链表结点的个数是不可预测的,所以不可能用以上的语句来产生链表的各结点。在以下产生整型单链表的程序中,使用了一个跟踪指针L,用循环的方法产生一个单链表:
PROGRAM EX01(INPUT,OUTPUT);
TYPE
Lp=^Litem;
Litem=RECORD
int:integer;
next:Lp
END;
VAR
head,L:Lp; i:integer;
BEGIN
NEW(head);L:=head; i:=1;
WRITE('请输入第',i,'结点值:');
READLN(L^.int);
WHILE not eof DO
BEGIN
i:=i+1;
NEW(L^.next);L:=L^.next;
WRITE('请输入第',i,'结点值: ');
READLN(L^.int)
END;
L^.next:=nil;
(*以上为单链表的产生,以下为单链表的输出*)
L:=head;
WHILE L^.next<>nil DO
BEGIN
WRITELN(l^.int);
L:=L^.next
END
END.