28
动动动动动动——动动 前前前前前前前前前前前前前前前前前前前前前前前前前前前前 前前前前前 前前前前前 。, 前前 前前前前 前前前前前前前前前前前前前前前 前前前前前前前前 一,,。 前前前前前前前前前前前前前 前前前前前前前前前前前前前前前前前前 前前前前前前 ,, 前前前前前前前前前前前前前 前前前前前前前前前 。, PASCAL 前 前前前前前 前前 前前前前前前 前前前前前 前前前前前前前前 前前前前前前前 前 前前前前前前 ,()()。 前前前前前前前前前前前前 .

动态数据类型 —— 指针

  • Upload
    luce

  • View
    204

  • Download
    0

Embed Size (px)

DESCRIPTION

动态数据类型 —— 指针. 前面介绍的各种简单类型的数据和构造类型的数据属于静态数据。在程序中,这些类型的变量一经说明,就在内存中占有固定的存储单元,直到该程序结束。. 与静态变量对应的是动态变量,在程序执行过程中可以动态产生或撤消,所使用的存储空间也随之动态地分配或回收。为了使用动态变量, PASCAL 系统提供了指针类型,用指针变量(静态变量)来指示动态变量(存储地址变量)。下面介绍如何利用指针建立动态数据结构. 一、指针的定义及操作. (一)指针类型和指针变量. - PowerPoint PPT Presentation

Citation preview

Page 1: 动态数据类型 —— 指针

动态数据类型——指针

前面介绍的各种简单类型的数据和构造类型的数据属于静态数据。在程序中,这些类型的变量一经说明,就在内存中占有固定的存储单元,直到该程序结束。

与静态变量对应的是动态变量,在程序执行过程中可以动态产生或撤消,所使用的存储空间也随之动态地分配或回收。为了使用动态变量, PASCAL 系统提供了指针类型,用指针变量(静态变量)来指示动态变量(存储地址变量)。下面介绍如何利用指针建立动态数据结构 .

Page 2: 动态数据类型 —— 指针

一、指针的定义及操作

(一)指针类型和指针变量在 pascal 中 , 指针变量 ( 也称动态变量 ) 存放某个存储单元的地址;也就是说, 指针变量指示某个存储单元。

指针类型的格式为 : ^基类型

说明 : ① 一个指针只能指示某一种类型数据的存储单元,这种数据类型就是指针的基类型,基类型可以是除指针、文件外的所有类型。

type pointer=^Integer;         var p1,p2:pointer;

定义了两个指针变量 p1 和 p2 ,这两个指针可以指示一个整型存储单元 ( 即 p1 、 p2 中存放的是某存储单元的地址,而该存储单元恰好能存放一个整型数据 ) 。

Page 3: 动态数据类型 —— 指针

② 和其它类型变量一样,也可以在 var 区直接定义指针型变量。

例如: var a:^real; b:^boolean;

又如: type person=record            name:string[20];            sex:(male,female);            age:1..100           end;      var pts:^person;

Page 4: 动态数据类型 —— 指针

③pascal 规定所有类型都必须先定义后使用,但只有在定义指针类型时可以例外,如下列定义是合法的:

type pointer=^rec;       rec=record         a:integer;         b:char        end;

Page 5: 动态数据类型 —— 指针

(二)开辟和释放动态存储单元

1 、开辟动态存储单元

在 pascal 中,指针变量的值一般是通过系统分配的,开辟一个动态存储单元必须调用标准过程 new 。

new 过程的调用的一般格式 :

New( 指针变量 )

功能:开辟一个存储单元,此单元能存放的数据的类型正好是指针的基类型,并把此存储单元的地址赋给指针变量。

Page 6: 动态数据类型 —— 指针

几点说明:

① 这实际上是给指针变量赋初值的基本方法。例如,设有说明:var p:^Integer;

这只定义了 P 是一个指示整型存储单元的指针变量,但这个单元尚未开辟,或者说 P 中尚未有值(某存储单元的首地址)。当程序中执行了语句 new(p) 才给p赋值,即在内存中开辟(分配)一个整型变量存储单元,并把此单元的地址放在变量p中。

示意如下图:

(a) 编译时给   (b) 执行 New(p) 后   (c)(b) 的简略表示  p 分配空间     生成新单元  ? 表示值不定  新单元的地址为 XXXX          内存单元示意图

Page 7: 动态数据类型 —— 指针

② 一个指针变量只能存放一个地址。如再一次执行 New(p) 语句,将在内存中开辟另外一个新的整型变量存储单元,并把此新单元的地址放在p中,从而丢失了原存储单元的地址。

③ 当不再使用p当前所指的存储单元时,可以通过标准过程 Dispose 释放该存储单元。

如: New(p); New(p);

Dispose(p);

Page 8: 动态数据类型 —— 指针

⒉ 释放动态存储单元

dispose 语句的一般格式 : dispose( 指针变量 );

功能:释放指针所指向的存储单元,使指针变量的值无定义。

Dispose(p);

如: New(p);

Page 9: 动态数据类型 —— 指针

(三)动态存储单元的引用

在给一个指针变量赋以某存储单元的地址后,就可以使用这个存储单元 .引用动态存储单元一般格式:<指针变量>^

说明:

① 在用 New 过程给指针变量开辟了一个它所指向的存储单元后,要使用此存储单元的唯一方法是利用该指针。

② 对动态存储单元所能进行的操作是该类型(指针的基类型)所允许的全部操作。

Page 10: 动态数据类型 —— 指针

例 1 设有下列说明 :   var p:^integer; i:integer;  画出执行下列操作后的内存示意图:     New(p); P^:=4;i:=p^;

(a) 编译时 (b) 执行 New 语句 (c) 执行 P^:=4 (d) 执行 i:=P^ 分配存储   单元         内存单元示意图

Page 11: 动态数据类型 —— 指针

(四)对指针变量的操作

⒈ 具有同一基类型的指针变量之间相互赋值例 2 设有下列说明与程序段 :   var p1,p2,p3:^integer;   begin   New(P1) ; New(P2); New(P3);    P1:=P2; P2:=P3;   end;

2 、可以给指针变量赋 nil 值nil 是 PASCAL 的关键字,它表示指针的值为 "空 "。

例如,执行:   p1:=ni1 后, p1 的值是有定义的,但 p1 不指向任何存储单元。

Page 12: 动态数据类型 —— 指针

3 、可以对指针变量进行相等或不相等的比较运算在实际应用中,通常可以在指针变量之间,或指针变量与 nil 之间进行相等 (= ) 或不相等 ( <>=的比较,比较的结果为布尔量。例 3 输入两个整数,按从小到大打印出来。

分析:不用指针类型可以很方便地编程,但为了示例指针的用法,我们利用指针类型。定义一个过程 swap 用以交换两个指针的值。

Type pointer=^integer;   var p1,p2:pointer;   procedure swap(var q1,q2:pointer);    var q:pointer;    begin     q:=q1;     q1:=q2;     q2:=q;    end;   begin    new(p1);new(p2);    write('Input 2 data:');readln(pq^,p2^);    if p1^>p2^ then swap(p1,p2);    writeln('Output 2 data:',p1^:4,p2^:4);   end.

Page 13: 动态数据类型 —— 指针

[ 例 1] 分别用简单变量和指针变量交换两个变量的值。

解:设两个变量 a,b 的值分别为 5, 8 (1) 用简单变量交换:

Program Exam101; 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.

Page 14: 动态数据类型 —— 指针

(2) 用指针变量交换:

Program Exam102; 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.

Page 15: 动态数据类型 —— 指针

5 5 5 5 58 8 8 8

a a ab b bc c c

① ② ③

a①将 cb②将 ac③将 b

(图中 表示赋值)

第 (2) 种方法,对各存储单元所保存的值并不改变,只是交换了指向这些单元的存储地址(指针值),可以简略地用如下图示说明:

第 (1) 种方法,直接采用变量赋值进行交换,(实际上给各存储单元重新赋值)其过程如下图所示:

5 5 58 8 8

a a ab b bc c c

① ② ③

Page 16: 动态数据类型 —— 指针

( 图中“ -- 〉”表示指向存储单元 )

指针类型的指针变量 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)。存储单元的值没有重新赋值,只是存放指针值的变量交换了指针值。

Page 17: 动态数据类型 —— 指针

[ 例 2] 利用指针对数组元素值进行排序。

分析:

① 定义一个各元素为指针类型的数组 a :② 定义一个交换指针值的过程 (swap) ;③ 定义一个打印过程 (print) ;④定义过程 (int) 将数组 b 的值赋给 a 数组各元素所指向的各存储单元。⑤过程 pixu 用交换指针值的方式,按 a 数组所指向的存储单元内容值从小到大地调整各元素指针值,实现“指针”排序;⑥按顺序打印 a 数组各元素指向单元的值 (a[ i ] ^ ) 。

Page 18: 动态数据类型 —— 指针

Program Exam2; 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;

Page 19: 动态数据类型 —— 指针

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;

Page 20: 动态数据类型 —— 指针

Begin { 主程序部分 } int; pixu; print; readln End.

Page 21: 动态数据类型 —— 指针

[ 例 3] 有 m 只猴子要选猴王,选举办法如下:所有猴子按 1..m 编号围坐成圆圈,从第一号开始按顺序 1,2, ..,n连续报数,凡报 n号的退出到圈外。如此循环报数,直到圈上只剩下一只猴子即当选为王。普通方法

经仔细分析,此题实质与筛选法求素数类似。

1 )开始时将 m 个下标变量的值均赋为 1 ,表示大家都在圈上。

2 )假设我们用变量 K 来计数,当报到 n 时(即 K 的值为 n )退出,将相应的下标变量的值改赋为 0 ,表示他已退出圈,其值不影响 K 的下一次计数。3 )连圈问题:计数时下标值从 1 开始依次增加,超 n 时重新赋为 1 ,这样做就连成圈了。

Page 22: 动态数据类型 —— 指针

Program monkey;Var a:array[1..100] of integer; I,k,p,x:integer;Begin readln(m,n); for I:=1 to n do a[i]:=1; k:=0;p:=0; while p<m-1 do { 当 P=m-1 时结束任务,即只有一个人还在圈上 } begin x:=0; { 每次计数时清零 } while x<n do begin k:=k+1; if k>m then k:=1; { 连成圈 } x:=x+a[k]; end; write(k,’ ‘); a[k]:=0; p:=p+1; end;End.

程序说明: K :数组下标值,为了连成圈 P :统计出圈个数 X :报数值

Page 23: 动态数据类型 —— 指针

[ 例 3] 有 m 只猴子要选猴王,选举办法如下:所有猴子按 1..m 编号围坐成圆圈,从第一号开始按顺序 1,2, ..,n连续报数,凡报 n号的退出到圈外。如此循环报数,直到圈上只剩下一只猴子即当选为王。用指针(环形链表)编程。

分析:①让指针 pon 指向的单元为记录类型,记录内容含有两个域:

编号变量 链指针变量 ② 用过程 crea 建立环形链; ③ 用过程 king 进行报数处理:每报数一次 t计数累加一次,当所报次数能被 n 整除,就删去该指针指向的记录,将前一个记录的链指针指向下一个记录。删去的指向单元(记录)应释放。直到链指针所指向的单元是同一单元,就说明只剩下一个记录。 ④打印指向单元记录中编号域( num) 的值。

Page 24: 动态数据类型 —— 指针

program Exam03; 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;

Page 25: 动态数据类型 —— 指针

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.

Page 26: 动态数据类型 —— 指针

1.请将下列八个国家的国名按英文字典顺序排列输出。 China( 中国 ) Japan( 日本 ) Cancda( 加拿大 ) Korea(朝鲜 ) England(英格兰 ) France( 法兰西 ) American( 美国 ) India(印度 )

2. 某医院里一些刚生下来的婴儿 ,都还没有取名字,全都统一用婴儿服包装,很难区分是谁的小孩。所以必须建立卡片档案,包含内容有编号、性别、父母姓名、床号。实际婴儿数是变动的,有的到期了,家长要抱回家,要从卡片上注销;新的婴儿出生,要增加卡片,请编程用计算机处理动态管理婴儿的情况。

习 题

Page 27: 动态数据类型 —— 指针

二、链表结构

设有一批整数 (12 , 56 , 45, 86 , 77 ,……, ) ,如何存放呢 ? 当然我们可以选择以前学过的数组类型。但是,在使用数组前必须确定数组元素的个数。如果把数组定义得大了,就会有大量空闲存储单元,定义得小了,又会在运行中发生下标越界的错误,这是静态存储分配的局限性。

指针类型可以构造一个简单而实用的动态存储分配结构――链表结构。

Page 28: 动态数据类型 —— 指针

下图是一个简单链表结构示意图:

说明:①每个框表示链表的一个元素,称为结点。

②框的顶部表示了该存储单元的地址 ( 当然,这里的地址是假想的 ) 。③每个结点包含两个域:一个域存放整数,称为数据域,另一个域存放下一个结点 ( 称为该结点的后继结点,相应地,该结点为后继结点的前趋结点 ) 的地址。④链表的第一个结点称为表头,最后一个结点表尾,称为指针域;⑤指向表头的指针 head 称为头指针 ( 当 head 为 nil 时,称为空链表 ) ,在这个指针变量中 存放了表头的地址。⑥在表尾结点中,由指针域不指向任何结点,一般放入 nil 。