Одним из общих случаев использования указателей является соединение связанных списков записи. Во многих простых приложениях типа баз данных вы можете размещать записи данных в массивах или типизированных файлах, но иногда требуется что-то более гибкое чем массив, который имеет фиксированный размер. Распределяя дина мические записи, так что каждое поле имеет запись, указывающую на следующие записи, вы можете построить список, содержащий столько элементов, сколько вам требуется.
Ссылочный тип
Чтобы хранить указатели, вам требуется переменная-указатель, а для создания переменной-указателя вам необходим ссылочный тип (или тип "указатель"). Простейшим ссылочным типом является стандартный тип с именем Pointer. Переменная типа Pointer - это общий (нетипизированный) указатель, то есть, просто адрес. Он не содержит информации о том, на что он указывает.
Таким образом, чтобы использовать тот же пример SomeNumber, вы можете присвоить его адрес переменной-указателю:
var SomeNumber: Integer; SomeAddress: Pointer; begin SomeNumber := 17; {присвоить SomeNumber значение} SomeAddress := @SomeNumber; {присвоить SomeAddress адрес} SomeAddress := Addr(SomeNumber); {другой способ получения адреса} end.
Нетипизированные указатели в Паскале не используются, поскольку они очень ограничены. Они наиболее полезны, когда указываемый элемент будет изменяться, так как нетипизированный указатель совместим с любым другим указателем. Типизированные указатели значительно более полезны, и как вы узнаете в следующем разделе, они более надежны.
Типизированные указатели
Обычно вы определяете ссылочные типы, которые указывают на конкретный вид элемента, например, целое значение или запись данных. Как вы далее увидите, можно извлечь преимущество из того факта, что указателю известно, на что он указывает. Чтобы определить типизированный указатель, вы можете описать новый тип, определенный символом каре (^), за которым следуют один или более идентификаторов. Например, чтобы определить указатель на Integer, вы можете сделать следующее:
type PIneger = ^Integer;
Теперь вы можете описать переменные типа PInteger. Если вы не собираетесь часто использовать ссылочный тип, то можете просто описать переменные, как указатели на уже определенный тип.
Например, если вы определили PInteger как ^Integer, то следующие описания переменной эквивалентны:
var X: ^Integer: Y: PInteger;
Разыменование указателей
До сих пор мы видели, как можно присваивать указателям значения, но если вы не можете получить значения обратно, польза от этого невелика. Разыменовав типизированный указатель, вы можете интерпретировать так, как если бы это была переменная типа, на которую он указывает. Чтобы разыменовать указатель, поместите символ каре (^) после идентификатора указателя.
Ниже показаны некоторые примеры разыменования указателя:
type PInteger = ^Integer;
var SomeNumber: Integer; { присвоить SomeNumber 17 } SomeAddress := @SomeNumber; { SomeAddress указывает на SomeNumber } Writeln(SomeNumber); { напечатать 17 } Writeln(SomeAddress); { не допускается; указатели печатать нельзя } Writeln(SomeAddress^); { напечатать 17 } AnotherAddress := SomeAddress; { также указывает на SomeNumber } AnotehrAddress^ := 99; { новое значение для SomeNumber } Writeln(SomeNumber); { напечатать 99 } end.
Наиболее важными строками здесь являются следующие:
AnotherAddress := SomeAddress; { также указывает на SomeNumber } AnotehrAddress^ := 99; { новое значение для SomeNumber }
Если вы поймете разницу между этими двумя операторами, то поймете основные моменты в использовании указателей. Первый оператор присваивает адрес переменной AnotherAddress; он сообщает ей, куда нужно указывать. Второй оператор присваивает новое значение элементу, на который указывает AnotherAddress. На рисунке графически показано, как изменяется переменная.
Как использовать указатели?
Теперь вы получили достаточно хорошее представление о том, в каких ситуациях вы можете использовать указатели, и можно рассмотреть их фактическое применение. В данном разделе охватываются следующие темы:
Распределение динамических переменных.
Освобождение выделенной для динамических переменных памяти.
Распределение и освобождение выделенных объемов памяти.
Проверка доступного в динамически распределяемой области пространства.
Borland Pascal предусматривает две пары процедур для выделения и освобождения памяти, распределяемой для динамических переменных. Чаще всего используются процедуры New и Dispose, которые отвечают большинству потребностей. Процедуры GetMem и FreeMem выполняют те же функции, но на более низком уровне.
8 8 8
| |