1 / 28

动态数据类型 —— 指针

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

luce
Download Presentation

动态数据类型 —— 指针

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. 一、指针的定义及操作 (一)指针类型和指针变量 在pascal中,指针变量(也称动态变量)存放某个存储单元的地址;也就是说, 指针变量指示某个存储单元。 指针类型的格式为:^基类型 说明: ①一个指针只能指示某一种类型数据的存储单元,这种数据类型就是指针的基类型,基类型可以是除指针、文件外的所有类型。 type pointer=^Integer;var p1,p2:pointer; 定义了两个指针变量p1和p2,这两个指针可以指示一个整型存储单元(即p1、p2 中存放的是某存储单元的地址,而该存储单元恰好能存放一个整型数据)。

  3. ②和其它类型变量一样,也可以在var区直接定义指针型变量。②和其它类型变量一样,也可以在var区直接定义指针型变量。 例如:var a:^real; b:^boolean; 又如:type person=recordname:string[20];sex:(male,female);age:1..100end;var pts:^person;

  4. ③pascal规定所有类型都必须先定义后使用,但只有在定义指针类型时可以例外,如下列定义是合法的:③pascal规定所有类型都必须先定义后使用,但只有在定义指针类型时可以例外,如下列定义是合法的: type pointer=^rec;rec=recorda:integer;b:charend;

  5. (二)开辟和释放动态存储单元 1、开辟动态存储单元 在pascal中,指针变量的值一般是通过系统分配的,开辟一个动态存储单元必须调用标准过程new。 new过程的调用的一般格式: New(指针变量) 功能:开辟一个存储单元,此单元能存放的数据的类型正好是指针的基类型,并把此存储单元的地址赋给指针变量。

  6. 几点说明: ①这实际上是给指针变量赋初值的基本方法。例如,设有说明:var p:^Integer; 这只定义了P是一个指示整型存储单元的指针变量,但这个单元尚未开辟,或者说P中尚未有值(某存储单元的首地址)。当程序中执行了语句new(p)才给p赋值,即在内存中开辟(分配)一个整型变量存储单元,并把此单元的地址放在变量p中。 示意如下图: (a)编译时给   (b)执行New(p)后   (c)(b)的简略表示p分配空间     生成新单元?表示值不定  新单元的地址为XXXX          内存单元示意图

  7. ②一个指针变量只能存放一个地址。如再一次执行New(p)语句,将在内存中开辟另外一个新的整型变量存储单元,并把此新单元的地址放在p中,从而丢失了原存储单元的地址。②一个指针变量只能存放一个地址。如再一次执行New(p)语句,将在内存中开辟另外一个新的整型变量存储单元,并把此新单元的地址放在p中,从而丢失了原存储单元的地址。 如: New(p); New(p); ③当不再使用p当前所指的存储单元时,可以通过标准过程Dispose释放该存储单元。 Dispose(p);

  8. ⒉释放动态存储单元 dispose语句的一般格式: dispose(指针变量); 功能:释放指针所指向的存储单元,使指针变量的值无定义。 如: New(p); Dispose(p);

  9. (三)动态存储单元的引用 在给一个指针变量赋以某存储单元的地址后,就可以使用这个存储单元. 引用动态存储单元一般格式:<指针变量>^ 说明: ①在用New过程给指针变量开辟了一个它所指向的存储单元后,要使用此存储单元的唯一方法是利用该指针。 ②对动态存储单元所能进行的操作是该类型(指针的基类型)所允许的全部操作。

  10. 例1 设有下列说明:var p:^integer; i:integer;  画出执行下列操作后的内存示意图:New(p); P^:=4;i:=p^; (a)编译时 (b)执行New语句(c)执行P^:=4 (d)执行i:=P^ 分配存储   单元         内存单元示意图

  11. (四)对指针变量的操作 ⒈具有同一基类型的指针变量之间相互赋值 例2 设有下列说明与程序段:var p1,p2,p3:^integer;beginNew(P1) ; New(P2); New(P3);P1:=P2; P2:=P3;end; 2、可以给指针变量赋nil值 nil是PASCAL的关键字,它表示指针的值为"空"。 例如,执行:p1:=ni1后,p1的值是有定义的,但p1不指向任何存储单元。

  12. 3、可以对指针变量进行相等或不相等的比较运算3、可以对指针变量进行相等或不相等的比较运算 在实际应用中,通常可以在指针变量之间,或指针变量与nil之间进行相等(=)或不相等(<>=的比较,比较的结果为布尔量。 例3 输入两个整数,按从小到大打印出来。 分析:不用指针类型可以很方便地编程,但为了示例指针的用法,我们利用指针类型。定义一个过程swap用以交换两个指针的值。 Type pointer=^integer;var p1,p2:pointer;procedure swap(var q1,q2:pointer);var q:pointer;beginq:=q1;q1:=q2;q2:=q;end;beginnew(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.

  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.

  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.

  15. 第(1)种方法,直接采用变量赋值进行交换,(实际上给各存储单元重新赋值)其过程如下图所示:第(1)种方法,直接采用变量赋值进行交换,(实际上给各存储单元重新赋值)其过程如下图所示: 第(2)种方法,对各存储单元所保存的值并不改变,只是交换了指向这些单元的存储地址(指针值),可以简略地用如下图示说明:

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

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

  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;

  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;

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

  21. [例3] 有m只猴子要选猴王,选举办法如下:所有猴子按1..m编号围坐成圆圈,从第一号开始按顺序1,2, ..,n连续报数,凡报n号的退出到圈外。如此循环报数,直到圈上只剩下一只猴子即当选为王。 普通方法 经仔细分析,此题实质与筛选法求素数类似。 1)开始时将m个下标变量的值均赋为1,表示大家都在圈上。 2)假设我们用变量K来计数,当报到n时(即K的值为n)退出,将相应的下标变量的值改赋为0,表示他已退出圈,其值不影响K的下一次计数。 3)连圈问题:计数时下标值从1开始依次增加,超n时重新赋为1,这样做就连成圈了。

  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:报数值

  23. 编号变量 链指针变量 [例3] 有m只猴子要选猴王,选举办法如下:所有猴子按1..m编号围坐成圆圈,从第一号开始按顺序1,2, ..,n连续报数,凡报n号的退出到圈外。如此循环报数,直到圈上只剩下一只猴子即当选为王。用指针(环形链表)编程。 分析: ①让指针pon指向的单元为记录类型,记录内容含有两个域: ②用过程crea建立环形链; ③用过程king进行报数处理:每报数一次t计数累加一次,当所报次数能被n整除,就删去该指针指向的记录,将前一个记录的链指针指向下一个记录。删去的指向单元(记录)应释放。直到链指针所指向的单元是同一单元,就说明只剩下一个记录。 ④打印指向单元记录中编号域(num)的值。

  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;

  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.

  26. 习 题 1.请将下列八个国家的国名按英文字典顺序排列输出。 China(中国) Japan(日本) Cancda(加拿大) Korea(朝鲜) England(英格兰) France(法兰西) American(美国) India(印度) 2.某医院里一些刚生下来的婴儿 ,都还没有取名字,全都统一用婴儿服包装,很难区分是谁的小孩。所以必须建立卡片档案,包含内容有编号、性别、父母姓名、床号。实际婴儿数是变动的,有的到期了,家长要抱回家,要从卡片上注销;新的婴儿出生,要增加卡片,请编程用计算机处理动态管理婴儿的情况。

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

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

More Related