第九章指针类型_第九章指针

2020-02-27 其他范文 下载本文

第九章指针类型由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“第九章指针”。

9.1 指针类型的声明

指针类型在任何语言中都是比较难以理解也是比较灵活的一种数据类型

指针常是它所指的变量的内存地址。声明指针类型的语法如下:

Type 〈指针类型标志符〉=^〈基类型〉;

其中,指针类型标志符即是指针类型名,基类型可以是简单类型,如整型,实型,字符型等,也可以是结构类型,如数组,记录,集合等。

指针类型声明示例:

Type

Tr = ^Integer;PI = ^real;Word = Record Name: String[10];Age: Integer;Scores: Real;End;BytePtr = ^Byte;WordPtr = ^Word;

上例中,声明了4个指针类型。其中,BytePtr是一个指向字节类型的数据;而WordPtr是一个指向记录类型Word的数据

Object Pascal不要求基类型一定是要在前面已声明的,也可以是一个标志符,然后在同一个模块内声明基类型。声明了指针类型后,就可以声明指针类型的变量,如:

Var

BP: BytePtr;WP: WordPtr;或:

Var

BP: ^Bytel;指针所指的基类型可以是简单类型,也可以是构造类型,如:

Type

Student = Record

Name: String;Age: Integer;Sex:(Man, Woman);End;Var

StuPtr: ^Student;

上例中,声明了一个指向记录类型的Student指针变量StuPtr,以后程序中就可以使用StuPtr^来表示记录类型Student的动态变量。要访问其中的Name字段,可以写成StuPtr^.Name。这里介绍动态变量的概念,动态变量的构成是用指针类型的变量标志符后加一个“^”符号,就构成指针所指向的基类型的动态变量,如上例中的StuPtr^就是Student的动态变量。

与通常的变量一样,一旦声明了指针变量,编译器将给指针分配存储单元,但存储单元中的值尚未确定。要想让指针指向确定的地址,必须通过赋值语句或New标准过程来实现。如:

Label 1,2,3,4,5;Var M: Integer;X1, X2: ^Integer;Begin 1: M:= 20;2: X1:= @M;3: New(X2);4: X2^:= 150;5: Dispose(X2);End;执行上述语句段时,若编译器给变量M分配的内存地址为$011001,那么,执行标号为3的语句后,指针变量X1和变量M的关系如下:

X1

$011001

$011001 20

即指针变量X1的值为变量M的内存地址。

执行语句3时,编译器首先在内存中分配适宜存放指针X2所指向的数据(这里为正数)的一组存储单元(假设为$01100A),然后将这组单元的首地址写入指针X2。这里X2^称为动态存储变量。使用New建立了动态存储变量后,它的值是不确定的,以后可以将某个整数值存储在该单元中。

X2

$01100A

$01100A ?

X2

$01100A

$01100A 150

执行语句5后,标准过程Dispose将释放由New分配的内存单元。在程序段中,标号过程New和Dispose应配对使用。当用New分配的动态存储空间不再使用时,应及时地释放所分配的存储空间,避免发生错误。如果分配存储空间时内存不够,将触发一个异常错误(EoutOfMemory),程序随即终止。上例中的运算符“@”和“^”专用于指针类型,分别称为取址和应用运算符。

9.2 指针的运算

Delphi提供了专门的过程和函数来操作指针,这些过程和函数是:

New过程,@操作符,PTR函数,GetMem过程

1)New过程

New是Object Pascal中的标准例程(在System单元中声明),用于在应用程序中为动态变量分配一块区域,并把该区域的地址赋给指针变量。所分配区域的大小由指针所指的类型决定。如果分配存储空间时内存不够,将触发一个异常错误(EoutOfMemory)

New过程的声明如下:

Procedure New(Var P: Pointer);其中P是一个指针变量,调用了New过程后,程序就可以用P^作为指针所指类型的动态变量。相应地,当程序不再需要使用动态变量时,就应当调用标准例程Dispose删除New创建的动态变量,并释放所分配的空间。程序示例如下:

Type

PlisEntry =^ TlistEntry;TlistEntry = Record

Next : PlistEntry;Text : String;Count: Integer;End;Var

List, P: PlistEntry;Begin ……

New(P);P^.Next := List;P^.Text := ’Hellow world’;

P^.Count := 1;List:= P;Dispose(P);…… End;

2)@操作符

@操作符是一个一元操作符,用于获得操作数的的地址,@后面的操作数可以是变量,过程,函数或类型中的方法,程序示例如下:

Procedure ChangeValue(X: Integer);Var

IntPtr: ^Integer;Begin

Intptr:= @X;Writeln(IntPtr^);IntPtr^:= 20;End;

如果主程序如下: Var

Param: Integer;Begin Param:= 10;ChangeValue(Param);Writeln(Param);{10} End;

上例中,ChangeValue过程首先声明了一个指向整数类型数的指针IntPtr,然后用@操作符取出X的地址赋予IntPtr指针,并显示IntPtr指针指向的数,最后改变这个数。

3)PTR函数

PTR 函数是Pascal中的标准例程,用于把一个指定的地址转换为指针,语法为:

Function Ptr(Addre: Integer): pointer;

其中,Addre是一个整数,用于表示一个32位地址,函数执行的结果是把32为地址转化为指针。

4)GetMem过程

GetMem 过程也是Pascal中的标准例程,类似于New,用于在应用程序中堆栈中为动态变量申请一块指定大小的区域,并把该区域的地址赋予指针变量。

语法为:

Procedure GetMem(var P:Pointer;Size:Integer);其中P是一个指针变量,Size指定区域的字节数。所分配区域的大小由指针变量P的基类型决定。如果在应用程序堆栈中没有足够的内存空间供分配,将触发EOutOfMemory异常。如果程序不再需要该动态变量时,可以调用标准例程FreeMem释放该变量分配的内存空间。程序如下:

Var

F: file;Size: Integer;Buffer: Pchar;Begin

AignFile(F, ’test.txt’);

Reset(F,1);Try

Size:= FileSize(F);GetMem(Buffer, Size);Try

BlockRead(F, Buffer^,Size);ProceFile(Buffer, Size);Finally

FreeMem(Buffer);End;Finally

CloseFile(F);End;End;

上例打开一个名字Test.txt为的文件,并把文件读入动态分配的缓冲区,缓冲区大小为文件的大小,然后对文件进行处理,最后释放动态分配的缓冲区,并关闭文件。Pascal中有一个特殊的保留字nil,这是一个空指针常量,当指针的值为nil时,表示指针当前没有指向任何动态变量。值为nil的指针变量不能访问动态变量。指针变量除了能被赋值外,还能进行相等或不相等的比较,比较只限于类型兼容的指针变量之间。当两个指针指向同一个对象时,指针才相等。

9.3 无类型指针

无类型指针是指指针变量在声明时没有指明基类型。

无类型指针在声明中只使用Pointer。如:

Var

PAnyPOint: Pointer;

指针PAnyPOint可以指向任何变量类型。无类型的指针的作用是它可以指向任何类型,但是,不能用指针变量符后加“^”的形式引用它的动态变量。要引用Pointer类型指针指向的变量,应先将其转换为确定的类型,如: Type Tpinte: ^Integer;Var

M, N: Integer;P: Pointer;Pt: Tpinte;Begin M:= 150;P:= @M;Pt:= Tpinte(P)N:= Pt^;End;

9.4 字符指针类型

字符指针类型即PChar 数据类型,是一个指向以NULL(不是零)字符结尾的字符串的指针。这种类型主要用于与外部函数中如在Windows API 中所用的函数兼容。与Pascal字符串不同,Windows和C字符串没有一个长度字节。取而代之的是它们0字节索引开始,以一个NULL结束。Pascal RTL字符函数根据长度决定存储在字符串变量中的字符数目。在Pascal中使用这些函数就需要PChar类型变量。内存将分配给变量并被所需函数使用。

除了PChar外,Delphi还包含PAnsiChar和PWideChar数据类型。 PAnsiChar数据类型是一个指向以NULL字符结尾的AnsiChar字符串的指针,在Delphi中,PCHAR等同于PAnsiChar。

 PWideChar数据类型是一个指向以NULL字符结尾的WideChar字符串的指针,用于UniCode字符集。

实际上,PAnsiChar和PWideChar数据类型的定义为: Type

PansiChar = ^AnsiChar;PwideChar = ^WideChar;Pchar = PansiChar;

字符串类型与PChar类型赋值兼容,即一个字符串可以直接赋给一个PChar类型的变量,如: var P: Pchar;…… Begin P:= ’Hello World’;End;

9.5 动态存储结构的实现

指针常用于描述动态存储结构的实现。

动态存储结构中常用的有链表,堆栈,队列等存储结构。可以把堆栈和队列看成特殊的链表

本节只是简单介绍一下如何利用指针和记录来实现链表结构。

链表是一组元素的序列,在这个序列中每个元素总是与他前面的元素相链接(第一个元素除外)。这种关系可以通过指针来实现。链表中的元素称为节点,第一个节点称为表头,最后一个称为表尾。指向表头的指针称为头指针,在这个头指针里存放着表头的地址。节点一般用记录来描述,描述节点的记录至少含有两个域,一个用来存放数据,该域的类型根据要存放的数据而定,称为值域;另一个用来存放下一个节点的地址,称为指针域。表尾不指向任何节点,其指针的值为NIL。如图:

应用Object LPascal的指针和记录类型,图示的链表可以声明如下: Type

Node:= Record;Data: Char;Next: ^Node;End;Var

Head: ^Node;或者: Type Link = ^Node;Node:= Record

Data: Char;Next: ^Node;End;Var

Head: Link;

链表中相邻节点的地址是不连续的。当表头指针失去了指向表头的地址后,就无法找到整个链表,从而不能再对链表进行操作。同样,当任一节点中的指针失去了下一个节点后,链表就会断开,后边的节点就会全部消失。

若让表尾节点原有的空指针指向表头节点,就成为循环链表。如果链表的各节点既有指向前一个节点的指针又有指向后一个节点指针,这时的链表就称为双向链表。

链表可以描述许多实际问题,区别只是链表的值域有所不同。

对链表的操作有查找,插入,删除等。对于插入和删除操作来说,链表是很实用的数据结构。不论在链表的什么位置插入或删除节点,只需修改相应的指针。不必像顺序存储的数组那样需要移动数组中的每个元素。但对于链表来说,只有指针对用户来说是可见的。因此,要访问链表中某个节点的数据,必须从头指针开始依次搜索要访问的元素。

队列和堆栈是特殊的链表。所谓队列就是一个先入先出表。在该表中只允许在表头插入节点,在表尾删除节点。向队列中插入节点称作入队,新节点入队后就成为队列的新表尾;从队列的表头删除节点称为出队,出队后,其后继节点成为表头。由于队列的插入和删除操作分别在两端进行,所以要删除的节点将是队列中最先进入的节点。堆栈则允许在链表的表头进行插入和删除操作。这里表头称为栈顶,另一端为栈底。向一个堆栈中插入新节点成为入栈或压栈,新节点插入后成为新的栈顶节点;从堆栈中删除节点称为出栈或退栈,它是把栈顶节点删除掉,是其相邻的节点成为新的栈顶。由于插入和删除仅在栈顶一端进行,后进栈的节点必然会先被删除,所以堆栈又称为先进后出表。

【例9-1】 下列是一个关于在链表中利用指针处理字符串的程序。

分析:该程序的功能是通过一个文本编辑框输入一个字符串,输入后将该字符串存入一个链表中。每输入一次,在链表中新添一个节点。输入一些字符串后,单击“显示”按钮将输入的所有字符串显示在一个列表框中。通过文本编辑框输入的所有字符串显示在一个列表框中。通过文本框输入要查找的字符串,然后单击“删除”,将该字符串从链表中删除。此时再按下“显示”按钮,显示新的链表中的数据。

设计时添加一个组件Edit, 名为Edit1,添加一个组件Memo,名为Memo1, 三个Button组件:“删除”名为btnDel,“显示”名为btnList,“退出”名为btnQuit。

为了实现程序的功能,先声明一个全程的链表结构类型并命名一个该类型变量。这些在窗体单元的实现部分的开始处进行实现:

implementation {$R *.dfm} type

pLink = ^Node;Node = record Data: string[30];Next: pLink;end;var

Head: pLink;

当程序创建窗体时进行变量Head的初始化:

procedure TForm1.FormCreate(Sender: TObject);begin

Head:= Nil;end;

当在文本编辑框中输入字符串并按下回车键后,在链表上添加一个新节点,并将输入的数据保存在该节点的数据域中,这个功能通过Edit1的OnKeyPre处理过程实现:

procedure TForm1.Edit1KeyPre(Sender: TObject;var Key: Char);var

tempP, P: pLink;begin

if Key = #13 then //如果按下回车键

begin

New(tempP);//创建一个节点

tempP^.Data:= Edit1.Text;tempP^.Next:= Nil;if Head = Nil then //如果链表为空表

begin

New(Head);//创建链表

Head:= tempP;end

else begin //如果链表不为空表 P:= Head;while P^.Next Nil do //找表尾

P:= P^.Next;P^.Next:= tempP;//将新节点添加到表尾

end;Edit1.Clear;//清空编辑框

Edit1.SetFocus;//置编辑框为活动焦点

end;end;

当按下“显示”时,在列表框ListBox1中显示链表中所有节点数据域中的数据:

procedure TForm1.btnListClick(Sender: TObject);var

P: pLink;begin

Memo1.Clear;P:= Head;While P Nil do

begin

Memo1.Lines.Add(P^.Data);P:= P^.Next;end;end;

在Edit1输入框中输入一个字符串后,单击“删除”,程序将按该字符串在链表中查找相应的节点,找到后删除该节点:

procedure TForm1.btnDelClick(Sender: TObject);var

P,P1: pLink;i: integer;begin

i:=0;P:= Head;if P = nil then

ShowMeage('链表为空')else if P^.Data = Edit1.Text then begin //如果链表的第一个节点为要删除的节点

Head:= P^.Next;P^.Next:= nil;P:= Head;end

else begin //如果链表的第一个节点不是要删除的节点

P1:= P^.Next;if P1 nil then

repeat

if P1.Data = Edit1.Text then

begin

P^.Next:= P1^.Next;//如果找到一个要删除的节点

P1:= P;i:= i+1;end

else begin

P:= P1;P1:= P1^.Next;end;Until P1 = nil;if i = 0 then ShowMeage('无匹配的字符串');end;end;

procedure TForm1.btnQuitClick(Sender: TObject);begin

Close;end;

单击“Run”按钮,编译运行程序后,在文本框中输入了一些字符串数据,然后单击“显示”按钮后,列表框中将显示输入的所有字符串。如图:

此时,在文本编辑框中输入一个要删除的数据,然后按下“删除”,匹配的数据将从链表中删除,此时按下“显示”,将在列表

《第九章指针类型.docx》
将本文的Word文档下载,方便收藏和打印
推荐度:
第九章指针类型
点击下载文档
相关专题 第九章指针 第九章 指针 类型 第九章指针 第九章 指针 类型
[其他范文]相关推荐
    [其他范文]热门文章
      下载全文