一、静态存储与动态存储
我们知道使用变量前必须首先在变量说明部分对变量进行定义,变量一经定义,编译时系统就会给这些变量分配相应的内存空间。这种分配称为静态存储分配。
动态存储分配是指事先不确定数据存储,在程序运行过程中根据实际需要动态申请所需存储空间,用完后及时将存储空间归还给系统。
内存中的每一个存储单元都有一个编号,也就是“地址”。存储单元中存放的是各种类型的数据,也就是存储单元的“内容”。一个存储单元的“内容”的写入或读出,是根据该存储单元的“地址”进行。另一方面,在计算机中,“地址”实际上是一个特殊的整数,因此“地址”可以看成是一种特殊的“内容”,可以存储到另一个存储单元中。这样,两个存储单元之间建立了一种联系。可见,“内容”和“地址”的概念是相对的,“地址”可以理解为“内容”,“内容”可以理解为“地址”,这主要取决于我们在理解时所处的角度。正确理解“地址”和“内容”两个概念及其辩证关系是学好动态存储分配的关键。
图10-1解释了上述关系。其中变量a是一个存储地址的变量,其自身的地址是2000;变量b是一个存储整数的变量,其地址是2020,可以将2020存储到a中,从而a 和b就建立了关系。同时,2020相对于a而言是“内容”,相对b而言则是“地址”。习惯上,为了清晰地表达a和b的关系,图10-1一般用图10-2代替,即在a中不再给出b的具体“地址“,而是从a的内部画一个箭头指向b,该箭头形状像一根针,所以又称为指针。指针实际上是“地址“,相应地,存储指针的变量称为指针变量(如a),指针箭头所指存储变量(如b)存储的数据的类型称为指针变量的基类型。
相对于静态存储分配,动态存储分配有两 个特点:
(1) 可以在运行时根据需要随用随要;
(2) 每次所申请的存储单元在内存中可以不连续,通过指针相互建立联系。
二、指针变量定义
为了表示指针变量和它所指向的变量之间的联系,在Pascal语言中使用了“^”表示指向。有两种说明方法:
法一:首先进行指针类型标识符的定义,其次是进行指针变量的定义。
指针类型标识符的定义形式如下:
type
指针类型标识符=^基类型标识符;
基类型可以是所学过的除文件类型以外的任何数据类型。Pascal规定,指针类型的定义应写在前面,指针所指向的基类型的类型定义应放在后面。
指针变量的定义形式如下:
var 指针变量名:指针类型标识符;
如:
源程序 |
|
type point=^integer; | point是指针类型标识符,经定义后,程序中就出现了一个名字叫point的数据类型,该类型的变量用于其存储整数类型变量的地址,也即该类型变量的内容是一个指针,指向一个整型变量。 |
var p1,p2:point; | p1,p2是point类型的变量,它们的值都是某个存储单元的地址,该地址中可以存储整型数据。 |
法二:可以在var区中直接定义,即:
var 指针变量名:^基类型标识符;
因此,上表的说明也可以写为:
var p1,p2:^integer;
两者效果是一样的。
总之,指针是用来实现动态存储分配的数据类型。指针变量的值是内存中某一个存储单元的地址,该地址单元中的值类型是指针变量的基类型。指针变量的基类型可以是除文件类型以外的其他数据类型。定义指针变量的方法有两种,要分清楚指针类型标识符和指针变量名。值 得说明的一点就是:指针变量本身是静态分配的,只说明它可以存储一个地址,但在程序运行前,其应该存储哪一个地址并没有确定,必须等到程序运行时根据申请到的地址来填写。从而实现动态的数据组织要求。
二、指针变量的基本使用方法
由于指针变量的内容(一个内存单元地址)是动态赋值的,我们事先不知道确切的地址,所以我们必须在需要时向系统提出申请,由系统给我们分配一个存储单元,然后获得该存储单元的确切地址并将其填写到指针变量中。当不再需要某一存储单元时,就必须将该存储单元空间还给系统,我们称之为释放。
pascal语言中规定了两个标准过程来实现存储单元空间的申请和释放。
1. 申请存储单元的过程:new(指针变量);
new(h);
说明:系统将自动分配一个存放数据的存储单元,并将该单元的地址赋给指针变量h 。存储单元的大小由h的基类型决定(如图10-3所示) 。
又如:
new(h);h^:=123;new(h);h^:=234;(实现过程如图10-4所示)
由上可知,对同一个指针变量h可以调用若干次new过程,但每一次调用对于指针变量h来说值是不一样的,也就是说指向的内存单元的地址是不同的。在这里不必去关心指针变量的值是多少,而是要关心该指针所指向的存储单元的值。
释放存储单元的过程:dispose(指针变量);
dispose(h);
系统收回指针变量h所指的内存单元,此时指针变量h所指的内存单元的值不可用,即指针变量h变成无确切指向。
指针变量的赋值和操作
利用new过程可以给一个指针变量赋予内存储单元的地址值,这个地址值我们并不需要了解,我们真正关心的是该指针变量所指的存储单元的内容。假设指针变量用p表示,Pascal用p^来表示指针变量p所指的内存单元的内容。对于p和p^我们都可以用赋值语句来实现。只是效果大小不相同。前者赋给的是地址,后者赋给的是数据内容。
假设p1,p2都是指针变量,其基类型为整型,对于语句p1:=p2;意思是将变量p2的值(存储单元的地址)赋给变量p1,这样变量p1和p2都同时指向变量p2所指的存储单元。(如图 10-5所示)
对于语句p1^:=p2^;意思是将变量p2所指的存储单元的值赋给变量p1所指的存储单元,这样变量p1,p2虽然所指的存储单元地址不同,但两个存储单元的值是相同的(如图10-6所示)。
有的时候我们并不需要指针变量指向其他存储单元,那么就可以将NIL赋给指针变量,即p1:=NIL;表示指针变量p1为空。任何类型的指针变量都可以被赋值为NIL。
总之,使用指针变量,我们需要注意两个值:一个是指针变量的值,表示内存单元的地址;另一个是指针变量所指内存单元的值。指针变量的值一般是通过new过程来实现的,有的时候也可能通过赋值语句实现两个指针共用一个内存单元,还可以将指针变量赋为NIl;而指针变量所指内存单元的值就需要像以前学过的一般变量一样,通过输入语句或赋值语句来实现。不要忘了应在指针变量后面加上来表示变量所指向的内存单元。
特别要注意的是,因为指针变量的值代表一个存储单元地址,它不能用输出语句进行打印,因此调试包括指针变量的程序是有一定难度的。在编程时我们必须要先仔细地将出现指针变量的每一步改变分析清楚,以免出现指针失效或不正确的情况。
习题一
1.利用指针对数组元素值进行排序。
解:①定义一个各元素为指针类型的数组a :
②定义一个交换指针值的过程(swap);
③定义一个打印过程(print);
④定义过程(int)将数组b的值赋给a数组各元素所指向的各存储单元。
⑤过程paixu用交换指针值的方式,按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 paixu; {排序}
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;
paixu;
print;
readln
End.
2.写出下列程序的运行结果
program aa;
var p1,p2,p3:^integer;
begin
new(p1);
p1^:=5;
new(p2);
p2^:=8;
p1^:=p1^+p2^;
writeln(p1^:8,p2^);
end.
program aa;
var p1,p2,p3:^integer;
begin
new(p1);
new(p2);
p1^:=9;
p2^:=5*(p1^ mod 5);
p3:=p1;p1:=p2;p2:=p3;
writeln(p1^:6,p2^:6,p3^);
end.
program aa;
type rec=record
a:integer;
b:char
end;
var p1,p2:^rec;
begin
new(p1);new(p2);
p1^.a:=90;
p1^.b:=’B’;
p2^.a:=100;
p2^.b:=’C’;
p1:=p2;
writeln(p1^.a:6,p2^.a)
end.
3.指针变量的值实际上是一特定结点在存储器中的——。
解答:若P是类型为^V的指针变量,P的值是P^所对应的存储空间的首地址,所以指针变量P的取值仅仅是表示地址值的整数类型。而P^是类型为S的变量,它的类型由V的类型决定,可以是整型,布尔型等简单类型,也可以是数组、记录等构造类型。
本题正确答案为地址。
4.指针变量所指向的变量的类型不可以是( )。
A)指针类型B)构造类型C)顺序类型D)整型
5输入两个整数,按从小到大打印出来。
分析:不用指针类型可以很方便地编程,但为了示例指针的用法,我们利用指针类型。定义一个过程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.