前面介绍的数组类型和集合类型有一个共同点,那就是在同一个数组或集合中的各有元素都必须具有相同的类型。如果要处理如下表所示的学生档案卡片上的数据,各栏目的数据类型不一样(学号,姓名,成绩…),需要用不同的类型表示。
学号 |
姓名 |
性别 |
出生年月 |
语文 |
数学 |
英语 |
平均分 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
为此,PASCAL系统定义了记录类型,可用来表示不同类型的数据。
[例5.14]建立一张学生情况表格,求出每位学生的平均成绩,并输出这张表格。
解:①定义记录类型:Date(表示日期记录,有三个域:day 日,mon月,yea年);
Studa(表示学生情况记录,有六个域:nu学号,na姓名,dd出生年月,se性别,s成绩,ave平均分);
②读入记录数据;
③计算学生的平均成绩;
④输出记录内容。
PASCAL程序:
Program Exam514;
Const n=2; m=3; {为了简单,人数N=2课目M=3}
Type Date=Record {定义Date(日期)记录类型}
day: 1..31; {域名day表示天,为子界型(1..31)}
mon: 1..12; {域名mon表示月,为子界型(1..12)}
yea: 1970..1999; {域名yea表示年,为子界类型}
End;
Studa=Record {定义Studa(学生情况)记录类型}
nu: string[5]; {域名nu表示学号,为字符串类型}
na: string[8]; {域名na表示姓名,为字符串类型}
dd: Date; {域名dd表示日期,为记录类型(Date)}
se: char; {域名se表示性别,为字符类型}
s: array[1..m] of real; {域名s表示成绩,为数组类型}
ave: real {域名s表示平均分,为实数类型}
End;
Sarr=array[1..n] of Studa; {定义Sarr为数组类型,各元素为记录类型}
Var Stu: sarr; {变量Stu为数组(Sarr)类型}
Procedure rrd(var stu:sarr); {定义输入和计算过程}
var i,k: integer;
t: real;
a: studa; {变量a为记录(Studa)类型}
begin
for k:=1 to n do
begin
with a,dd do {开域语句,打开当前记录a和dd,进行以下操作}
begin
write(k:2,' nu: '); readln(nu); {输入学号}
write(k:2,' na: '); readln(na); {输入姓名}
write(k:2,' se: '); readln(se); {输入性别}
write(k:2,' day: '); readln(day); {输入出生日}
write(k:2,' mon: '); readln(mon); {输入出生月}
write(k:2,' yea: '); readln(yea); {输入出生年}
t:=0;
for i:=1 to m do {输入m科的成绩分}
begin
write('s[',i,']='); read(s[ i ]);
t:=t+s[ i ] {累加总分}
end;
readln;
ave:=t/m; {计算平均分}
stu[k]:=a {将当前记录存入stu[k]中}
end;
end
end;
Procedure print1; {打印表格线}
var i: integer;
begin
for i:=1 to 60 do write('-');
writeln;
type 记录类型名 = record 域名:类型; … 域名:类型; end;
|
end;
Procedure print2; {打印表头和表格栏目}
begin
writeln(' ':18,'________ table _________' );
print1;
write(' num.', ' ':6, 'name', ' ':7, 'mm/dd/yy', ' ':4 );
writeln('sex ', 'Chin', ' ':2, 'math', ' ':2, 'Engl', ' ':2, 'vaer' );
print1
end;
Procedure print3(stu : sarr); {打印记录数据}
var i, j : integer;
begin
print2;
for j:=1 to n do
with stu[ j ], dd do
begin
write(nu:5, na:9, ' ':8, mon:2, '/ ', day:2, '/ ',
yea:4, ' ' );
write(se:3, ' ' );
for i:=1 to m do write(s[ i ]:6:1);
writeln(ave:6:1);
print1
end
end;
Begin
rrd(stu);
print3(stu);
readln
end.
程序自定义Date记录类型,用来表示学生的出生年月日,含三个分量(称为域):
day (日) 为子界类型(1..31);
mon(月) 为子界类型(1..12);
yea (年) 为子界类型(1970..1999);
自定义Stuta记录类型,用来表示学生情况,含六个域:
nu(学号) 为字符类型(string [5] 为5个字符);
na(学号) 为字符类型(string [8] 为8个字符);
dd(出生年月日) 是前面所定义的date记录类型;
se(性别) 是char字符类型 ;
s(表示学生成绩)是数组类型,各元素为real实型;
ave (学生平均分)是real实型 ;
程序定义的数组sarr的每个元素为Stuta记录(每个记录相当于一张学生情况卡片,整个数组相当于全体学生的情况卡片)。
自定义记录类型的一般格式为:
访问记录中的分量,有两种方式:
① 记录名.域名 例如对记录a中的nu赋值,可写成
a.nu:=1008; {将1008赋给记录a中的分量nu}
② with记录名 do 语句 开域语句,with后面可同时打开多个记录,
例如:with a, dd do语句; { a, dd都是记录名}
[例5.15]利用记录类型将N个数由大到小排序,输出排序结果并显示每个数原来的位置序号。
PASCAL程序:
Program Ex59;
Type dd=Record {定义DD记录类型}
ii:integer; {域名ii表示数}
id:integer {域名id表示数ii的位置序号}
End;
Var a:array[1..100]of dd;
i,j,k,n:integer;
t:dd;
Begin
Write('Please Input Number of elements:');
read(n);
writeln('Input elements:');
for i:=1 to n do
begin
read(a[i].ii);
a[i].id:=i;
for j:=1 to i-1 do
if a[j].ii<a[i].ii then
begin
t:=a[i];
for k:=i-1 downto j do
a[k+1]:=a[k];
a[j]:=t;
end;
end;
for i:=1 to n do
begin
write(a[i].ii:5,'(',a[i].id:2,')');
if i mod 10=0 then writeln
end;
readln;writeln;
End.
PASCAL系统自定义文件类型,可非常方便地实现对外存贮器的存取使用。常用的文件类型有顺序文件(File}和文本文件(Text)。
[例5.16]建立一个由1到50连续整数为分量的顺序文件。
解:①定义顺序文件类型(file);
②说明文件变量名;
③用assign过程指派一个文件变量和实际磁盘文件名对应;
④建立一个实际文件;
⑤将变量的值写入文件中;
⑥关闭文件。
Program Exam516;
type fdata=file of integer; {定义文件类型}
var fd: fdata; i, k : integer; {fd为文件变量}
begin
assign(fd, ’a: files’); rewrite(fd); {指派并建立文件}
for I:=1 to 50 do write(fd, i ); close(fd); {写文件,最后关闭文件}
end.
[例5.17]输出一个由1到50连续整数为分量的顺序文件。
解:①定义顺序文件类型(file);
②说明文件变量名;
③用assign过程指派文件变量和实际磁盘文件名对应;
④打开与文件变量对应的磁盘文件,并将文件分量指针设置在开头位置;
⑤读出指针所指的文件分量,赋给变量;
⑥关闭文件。
Program Exam517;
type fdata=file of integer; {定义文件类型}
var fd: fdata; i, k : integer; {fd为文件变量}
begin
assign(fd, ’files’); reset(fd); {指派并恢复文件指针}
for I:=1 to 50 do read(fd,i); close(fd); {读文件,最后关闭文件}
end.
顺序文件在磁盘上以二进制形式存储,所以又称为二进制文件。
另一种常用的文件类型是文本文件,以ASCII码的形式存储,比较通用,可以用DOS命令TYPE显示文件内容。
[例5.18]建立 由50个随机整数组成、文件名为f1.dat的TEXT文件。
解:①定义文件变量f为文本文件类型(TEXT);
②指派f与磁盘(指定盘符a:)上的f1.dat对应;
③建立文件;
④产生1~50个随机整数,写入文件中;
⑤关闭文件。
Program Exam518;
const
n=50;
var
s: integer; {s为rr型}
f: text; {f为text类型}
i: integer;
begin
assign(f, ’a: f1.dat’ ); {用文件变量f与a驱磁盘上f1.dat文件对应}
rewrite(f); {建立与f对应的实际文件名}
randomize; {初始化随机函数}
for I:=1 to n do
begin
s:=random(99)+1; {产生一个100以内的随机整数赋给s}
write(f, s:6); {将s写入文件中}
if I mod 5=0 then writeln(f) {每行写5个数据}
end;
close( f ) {关闭文件}
end.
[例5.19]读出a驱上磁盘文件f1.dat,并输出打印文件内容。
解:①定义文件变量f为text类型;
②定义输出打印过程;
③定义读文件过程。
Program Exam519;
const
n=50;
var
s: array[1..n] of integer;
f: text; {文件变量f为文本类型(text)}
procedure pf ; {输出打印过程}
var i: integer;
begin
for I:=1 to n do
begin
write(s[ i ]:6); {打印S数组元素}
if I mod 10=0 then writeln
end;
writeln
end;
procedure rf ; {读出文件的过程}
var i: integer;
begin
assign(f, 'a: f1.dat' ) ; {指派a盘上f1.dat文件}
reset(f ) ; {打开并恢复文件指针}
i:=0;
while not (eof ( f ) ) do {当文件没有结束时就做以下语句}
begin
if not(eoln(f)) then {如果不是行尾就读入数据}
begin
inc( i ); read(f, s[ i ]); {读入数据到数组S中}
end
else readln(f ); {否则就换行读}
end;
close(f ) {关闭文件}
end;
begin
rf ; {读文件}
pf {输出}
end.
文件类型常用指令表
操 作 |
格 式 |
注 释 |
指派文件 |
assign(文件变量, ’路径: 文件名’ ); |
将文件变量指定为实际文件 |
建立文件 |
rewrite(文件变量); |
建立一个文件 |
写文件 |
write(文件变量,变量); |
将变量值写入文件分量 |
打开文件 |
reset(文件变量); |
打开文件,指针恢复到开头 |
读文件 |
read(文件变量,变量); |
读文件分量值赋给变量 |
关闭文件 |
close(文件变量); |
|
文尾函数 |
Eof(文件变量) |
判断所读文件结束时为ture |
行尾函数 |
Eoln(文件变量) |
判断行结束时为 ture |
习题5.4
1.将2 ~ 500范围的素数存入a: \ prin中。
2.读出a: \ prin文件,并按每行8个数据的格式打印。
3.用随机函数产生100个三位数的随机整数,存入文件名为 ff.dat中,然后读出该文件内容,进行从大到小排序,将排序后的数据按每行10个的格式显示,并存入文件名为fff.dat中。
4.将一段英文句子存入Eng.dat文件中(以每句为一行),然后读出并显示。(也可用汉语拼音代替英文句子)
5.建立N个学生档案卡,每张卡包含:编号、姓名、性别、年龄、三科成绩的等级(分A、B、C三档),编程示例输出显示。
第五节 指针类型与动态数据结构
前面所学的变量都具有共同的特点:系统依据程序说明部分获得变量名和类型信息,即为变量分配对应大小的存储空间,在程序执行过程中,各变量对应的存储空间始终存在且保持不变,这些变量均称为静态变量。
与静态变量对应的是动态变量,在程序执行过程中可以动态产生或撤消,所使用的存储空间也随之动态地分配或回收。为了使用动态变量。PASCAL系统提供了指针类型,用指针变量(静态变量)来指示动态变量(存储地址变量)。下面介绍如何利用指针建立动态数据结构。
[例5.19] 分别用简单变量和指针变量交换两个变量的值。
解:设两个变量a,b的值分别为5, 8
(1)用简单变量交换:
Program Exam519;
const a=5; b=8; {常量}
var c: integer;
begin
c:=a; a:=b; b:=c; {用简单变量交换}
Type 指针类型名 = ^ 基类型;
|
writeln(’a=’:8, a, ’b=’:8, b );
readln
end.
(2)用指针变量交换:
Var 指针变量名 : ^ 基类型;
|
Program Exam519_1;
type pon= ^ integer; {pon为指针类型}
var a,b,c: pon; {a,b,c为指针变量}
begin
New(指针变量);
|
new(a ); new(b ); new(c ); {开辟动态存储单元}
a ^ :=5; b ^ :=8; {给a,b指向的存储单元赋值}
c:=a; a:=b; b:=c; {交换存储单元的指针}
writeln('a=':8, a ^ , ‘b=':8, b ^ ); {输出a,b所指单元的值}
Dispose(指针变量);
|
readln
End.
指针变量名 ^
|
第(1)种方法,直接采用变量赋值进行交换,(实际上给各存储单元重新赋值)其过程如下图所示:
第(2)种方法,对各存储单元所保存的值并不改变,只是交换了指向这些单元的存储地址(指针值),可以简略地用如下图示说明:
(图中“—→”表示指向存储单元)
指针类型的指针变量a,b存有各指向单元的地址值,将指针交换赋值步骤为:
①将c:=a (让c具有a的指针值);
②将a:=b (让a具有b的指针值);
③将b:=c (让b具有c的指针值);
最后输出a,b所指向存储单元(a ^ 和b ^ )的值,此时的a指向了存储整数8的单元(即a ^ = 8),b指向了存储整数5的单元(即b ^ = 5)。存储单元的值没有重新赋值,只是存放指针值的变量交换了指针值。
程序Exam519_1 Type pon= ^ integer; 是定义指针类型,一般格式为:
基类型就是指针所指向的数据元素的数据类型。也可以将类型说明合并在变量说明中直接定义说明:
例如Exam519_1中类型说明与变量可以合并说明为:
Var a,b,c: ^ integer; {与程序中分开说明的作用相同}
定义了指针变量的类型之后,必须调用New过程开辟存储单元,调用格式如下:
如果不再需要指针变量当前所指向的存储单元,可调用Dispose过程释放所占用的存储单元,Dispose和New的作用正好相反,其格式为:
指针所指向的存储单元用如下形式表示:
程序中的a ^ 表示指针变量a所指向的存储单元,与指针变量的关系如下图所示:
|
在程序中对所指存储单元(为a ^)可以视作简单变量进行赋值计算和输出。
如:a ^ := 5
[例5.20] 利用指针对数组元素值进行排序。
解:①定义一个各元素为指针类型的数组a :
②定义一个交换指针值的过程(swap);
③定义一个打印过程(print);
④定义过程(int)将数组b的值赋给a数组各元素所指向的各存储单元。
⑤过程pixu用交换指针值的方式,按a数组所指向的存储单元内容值从小到大地调整各元素指针值,实现“指针”排序;
⑥按顺序打印a数组各元素指向单元的值(a[ i ] ^ )。
Program Exam520;
const n=8;
b: array[1..n] of integer
=(44,46,98,86,36,48,79,71);
type pon= ^ integer;
var a: array[1..n] of pon;
Procedure swap(var p1, p2: pon); {交换指针}
var p: pon;
begin
p:=p1; p1:=p2; p2:=p
end;
Procedure print; {打印数组各元素指向单元(a[ i ] ^ )的值}
var i: integer;
begin
for i:=1 to n do write(a[ i ] ^ :6);
writeln; writeln;
end;
Procedure int; {将数组b的值赋给a数组各元素所指向的存储单元}
var i: integer;
begin
for i:=1 to n do
begin
new(a[ i ]);
a[ i ] ^ :=b[ i ];
end;
print;
end;
Procedure pixu; {排序}
var i,j,k: integer;
begin
for i:=1 to n-1 do
begin
k:=i;
for j:=i+1 to n do
if a[j] ^ < a[k] ^ then k:=j;
swap(a[k], a[ i ])
end
end;
Begin
int;
pixu;
print;
readln
End.
[例5.21] 有m只猴子要选猴王,选举办法如下:
所有猴子按1 . . m编号围坐成圆圈,从第一号开始按顺序1, 2, . . , n连续报数,凡报n号的退出到圈外。如此循环报数,直到圈上只剩下一只猴子即当选为王。用指针(环形链表)编程。
解:①让指针pon指向的单元为记录类型,记录内容含有两个域:
|
编号变量 链指针变量
|
②用过程crea建立环形链;
③用过程king进行报数处理: 每报数一次t计数累加一次,当所报次数能被n整除,就删去该指针指向的记录,将前一个记录的链指针指向下一个记录。删去的指向单元(记录)应释放。直到链指针所指向的单元是同一单元,就说明只剩下一个记录。
④打印指向单元记录中编号域(num)的值。
program Exam521;
type pon= ^ rec; {指向rec类型}
rec=record {rec为记录类型}
num: byte; {域名num为字节类型}
nxt: pon {域名nxt为pon类型}
end;
var hd: pon;
m, n: byte;
procedure crea; {建立环形链}
var s,p: pon;
i: byte;
begin
new(s); hd:=s; s ^ . num :=1; p:=s;
for i:=2 to n do
begin
new(s); s ^ . num :=i; p ^ . nxt:=s; p:=s
end;
p ^ . nxt :=hd
end;
procedure king; {报数处理}
var p,q: pon;
i, t: byte;
begin
p:=hd; t:=0; q:=p;
repeat
p:=q ^ . nxt; inc(t);
if t=n then
begin
q ^ . nxt :=p ^ . nxt; dispose(p); t:=0
end
else q:=p
until p=p ^ . nxt;
hd:=p
end;
begin
write('m, n='); readln(m, n); {输入m只猴,报数到n号}
crea;
king;
writeln('then king is :', hd ^ . num);
readln
end.
习题5.5
1.请将下列八个国家的国名按英文字典顺序排列输出。
China(中国) Japan(日本) Cancda(加拿大) Korea(朝鲜)
England(英格兰) France(法兰西) American(美国) India(印度)2.某医院里一些刚生下来的婴儿,都还没有取名字,全都统一用婴儿服包装,很难区分是谁的小孩。所以必须建立卡片档案,包含内容有编号、性别、父母姓名、床号。实际婴儿数是变动的,有的到期了,家长要抱回家,要从卡片上注销;新的婴儿出生,要增加卡片,请编程用计算机处理动态管理婴儿的情况。