Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
delphi / песни о паскале.pdf
Скачиваний:
63
Добавлен:
26.03.2016
Размер:
5.16 Mб
Скачать

 

 

Глава 51

 

 

 

Указатели в море памяти

 

 

 

 

 

 

type PInt = ^Integer;

{ тип указателя на целое }

 

 

PChar = ^Char;

{ тип указателя на символ }

 

 

PString = ^String;

{ тип указателя на строку }

 

 

 

 

 

Как всегда, имя объявляемого типа выбираем по вкусу. Здесь лучше придерживаться традиций, а они рекомендуют начинать названия типов-указателей с буквы «P» (от Pointer — «указатель»).

Объявив тип, можно объявить затем переменные этого типа, например:

var p1, p2 : PInt;

{

два указателя на целое }

p3

: PChar;

{

указатель на символ }

 

 

 

 

Копирование указателей, пустой указатель

Я сказал, что указатель содержит адрес переменной, стало быть, это число? Да, но не совсем обычное. Нас интересует не само это число, а лишь то, на что оно указывает.

Если нужны несколько указателей на одну и ту же переменную, указатели копируют, например:

P1

:=

@X1;

{

В указатель P1 заносится адрес переменной X1

}

P2

:=

P1;

{

Оба указателя содержат адрес переменной X1 }

 

 

 

 

 

 

 

Теперь оба указателя ссылаются на переменную X1 (хотя сам по себе адрес переменной X1 нас не интересует). Но копировать можно лишь указатели одного типа, — за этим соответствием следит компилятор.

А на что ссылается указатель, которому не присвоено значение? Как любая неинициализированная переменная, он содержит мусор и указывает «пальцем в небо». Для пометки временно не используемого указателя ему присваивают специальное значение NIL. Это зарезервированное слово языка — подобие нуля для чисел. Значение NIL можно присвоить указателю любого типа, например:

p1 := nil; { p1 – пустой указатель на целое } p3 := nil; { p3 – пустой указатель на символ }

Указатели похожи на письма, а переменные — на дома, куда эти письма адресованы (рис. 117).

401

Глава 51

Указатели в море памяти

1

1

Указатели

 

 

Пустой указатель NIL

 

 

(чистый конверт)

Переменные

1

2

 

 

Рис. 117 – Указатели подобны письмам

Судя по рис. 117, жителям первого дома повезло, — им адресованы два письма. Третий конверт — без адреса, он пуст и подобен указателю, содержащему

NIL.

Сравнение и проверка указателей

Поскольку указатели — это не обычные числа, их нельзя вычитать, складывать и сравнивать на «больше» или «меньше». Зато можно сравнивать на равенство и неравенство. В таком сравнении есть смысл: ведь если непустые указатели равны, то ссылаются на одну и ту же переменную. Вот примеры правильных сравнений.

if p1=p2 then ...

if p1<>p2 then ...

if p3=nil then ...

Сравнением с NIL выясняется, свободен ли указатель или ссылается на чтото. Но значение NIL в указатель должен занести программист, само оно там не появится!

Проверить незанятость указателя можно как сравнением с NIL, так и функцией Assigned. Она принимает указатель любого типа, а возвращает булев результат. Вот примеры её применения.

402

 

 

Глава 51

 

 

 

Указатели в море памяти

 

 

 

 

 

 

p1 := @X; p3 := nil;

 

 

 

Writeln (Assigned(p1));

{ true }

 

 

Writeln (Assigned(p3));

{ false }

 

 

 

 

 

Функция Assigned возвращает FALSE, если указатель содержит NIL.

Разыменование указателей

Этим неуклюжим словом — разыменование — названа операция, обратная взятию адреса. Разыменование превращает указатель в переменную, на которую он ссылается. Операция обозначается следующей за указателем стрелкой вверх «^», вот пример:

p1 := @X;

{ назначение адреса указателю P1 }

X := 25;

 

 

Writeln (p1^);

{

25 }

X := 100;

 

 

Writeln (p1^);

{

100 }

 

 

 

Здесь показано, что с изменением переменной X меняется и значение P1^. Иначе говоря, если P1=@X, то P1^=X (а верно ли обратное?).

Итак, указатели дают ещё один способ доступа к переменным, к которым мы обращаемся по именам. В чем же выгода от указателей? — пока её не видно. Но, проявив немного терпения, вы изведаете всю их мощь.

Нетипичный указатель

Типы указателей соотносятся с типами данных, на которые они ссылаются. Но порой нужен универсальный указатель, способный ссылаться на что угодно. Такой указатель объявляют как Pointer, — указатели этого типа нельзя разыменовать, но можно сравнивать между собой и со значением NIL.

var P1, P2 : pointer;

N : integer;

S : string;

begin

 

 

 

P1:= @N;

P2:= @S;

 

 

if P1=P2 then Writeln('Указатели совпадают'); if P1<>nil then Writeln('Указатель не пустой');

end.

Впрочем, такой указатель можно привести к любому другому типу указателя (преобразовать тип указателя), и тогда возможно разыменование полученной конструкции, например:

403

 

 

 

 

Глава 51

 

 

 

 

 

Указатели в море памяти

 

 

 

 

 

 

 

type

PInt = ^integer;

{ тип указателя на целое }

 

 

var

P : pointer;

N : integer;

 

 

 

...

 

 

 

 

 

P:= @N;

 

 

 

 

 

Writeln( PInt(P)^ );

{ печатается значение N }

 

 

 

 

 

 

 

Примеры с указателями

Рассмотрим пару несложных программ, поясняющих работу указателей, испытайте их на своем компьютере.

{ P_51_1 – Указатели }

 

var A, B, C : integer;

{ целые числа }

p1, p2, p3 :^integer;

{ указатели на целые числа }

begin

 

{Присвоение значений переменным } A:= 10; B:= 20; C:= 30;

{Последовательное переключение одного указателя на разные переменные } p1:= @A; Writeln(p1^);

p1:= @B; Writeln(p1^); p1:= @C; Writeln(p1^);

{Настройка трех указателей на одну переменную }

p1:=@B; p2:=p1; p3:=p1; Writeln(p1^:6, p2^:6, p3^:6);

{ Арифметические действия через указатели } C:= 2 * p1^;

Writeln(C); { C= 2 * B = 40 }

Readln;

end.

Результат работы этой программы таков.

10

20

30

20 20 20

40

Здесь опять убеждаемся, что разыменованный указатель равнозначен переменной, на которую он ссылается. С ним выполняют те же действия, что и с переменной: ввод, вывод, арифметические операции и так далее.

404

Глава 51

Указатели в море памяти

В программе P_51_2 мы ещё раз увидим это, а вдобавок исследуем размеры указателей на переменные разных типов, — отличаются ли они?

{ P_51_2 – Указатели разных типов, размеры указателей }

type PBool= ^boolean;

{ Тип указателя на булевскую переменную }

PInt = ^integer;

{ Тип указателя на целое число }

PStr = ^string;

{ Тип указателя на строку }

var B : boolean;

 

 

I : integer;

 

 

S : string;

 

 

pB : PBool;

{ Указатель на булевскую переменную }

pI : PInt;

{ Указатель на целое число }

pS : PStr;

{ Указатель на строку }

begin

 

 

{Настройка указателей на переменные } pB := @B; pI := @I; pS := @S;

{Присвоение значений переменным через указатели } pB^ := true;

pI^ := 10;

pS^ := 'Hello!';

{Распечатка значений переменных }

Writeln(B:6, I:6, S:10);

{ Исследование размеров типов и указателей на них } Writeln('Boolean = ',SizeOf(Boolean):6, SizeOf(PBool):6); Writeln('Integer = ',SizeOf(integer):6, SizeOf(PInt ):6); Writeln('String = ',SizeOf(String ):6, SizeOf(PStr ):6); Readln;

end.

Вот «продукция» этой программы.

true 10

 

Hello!

Boolean

= 1

4

Integer

=

2

4

String

=

256

4

 

 

 

 

Любопытны три последних строки. Они показывают, что размеры указателей на переменные всех типов одинаковы и для 32-разрядных систем составляют 4 байта (подобно тому, как размер конверта не зависит от размера дома, куда он адресован).

В следующей главе мы пожнем первые плоды от применения указателей, а пока подведем итоги.

405

Соседние файлы в папке delphi