Под переменную N и переменную U будет отведено по 4 байта памяти в сегменте данных. Переменные будут содержать адрес какой–либо ячейки памяти, расположенной в динамической области. Но прежде, чем переменная ссылочного типа примет значение, необходимо в ходе выполнения программы выполнить специальную процедуру. Ссылка представляет собой адрес начала, т.е. первой ячейки, некоторого места в памяти, выделенного для объекта базового типа. Переменная U будет содержать адрес первой ячейки, выделенной под массив W в динамической области памяти.
Ссылочные переменные в языке Турбо Паскаль
Описание ссылочного типа выглядит следующим образом:
Type
Ptr = ^t,
где t – стандартный или заранее описанный тип данных, называемый базовым типом. Сами
адреса будут храниться в ссылочных переменных, которые описываются обычным образом,
например, Var P : Ptr. Такие переменные для хранения адресов динамической памяти
называются ссылками или указателями.
Пример.
Type
Pint = ^Integer; W = array [1..20] of Real; p = ^W;
Var
N : Pint; U : p;
Под переменную N и переменную U будет отведено по 4 байта памяти в сегменте данных.
Переменные будут содержать адрес какой–либо ячейки памяти, расположенной в
динамической области. Но прежде, чем переменная ссылочного типа примет значение,
необходимо в ходе выполнения программы выполнить специальную процедуру. Ссылка
представляет собой адрес начала, т.е. первой ячейки, некоторого места в памяти,
выделенного для объекта базового типа. Переменная U будет содержать адрес первой
ячейки, выделенной под массив W в динамической области памяти.
В объявлениях ссылочных типов после символа “^” может стоять только простое имя типа.
В случае сложных имен используется переопределение типов, как в приведенном примере.
Указатели, связанные с адресами значений конкретных базовых типов, называются
типизированными. N и U – типизированные указатели. В Турбо Паскале можно объявлять
указатель и не связывать его при этом с каким–либо конкретным типом данных. Такие
указатели называются нетипизированными. Описание нетипизированных указателей
осуществляется с помощью служебного слова Pointer, например, Var P : Pointer.
Адрес хранится как два слова: одно из них определяет сегмент, а другое – смещение.
Значение указателя не может быть в явном виде выведено на экран или печать. Его надо
предварительно расшифровать
Так как значение указателя состоит из двух слов (Word), хранящих сегмент и смещение,
можно вывести их в отдельности, используя функции Seg и Ofs:
Writeln(‘Сегмент ’, Seg(p), ‘ смещение ‘, Ofs(p));
Указатели могут обмениваться значениями через оператор присваивания.
Типизированному указателю можно присвоить значение либо указателя того же типа, что и
он сам, либо нетипизированного указателя. Если, например,
Var P1, P2 : ^Integer;
P3 : ^Real;
PP : Pointer;
то присваивание P1 := P2 вполне допустимо, в то время как P1 := P3 запрещено, поскольку
P1 и P3 указывают на разные типы данных. Это ограничение не распространяется на
нетипизированные указатели. Можно записать PP := P3; P1 := PP и достичь нужного
результата. Присутствие в программе таких переприсвоений говорит о том, что
программист делает это осознано и в программе действительно нужны такие действия.
Указателю можно присвоить значение Nil. Nil – это предопределенная константа типаPointer, соответствующая адресу 0000:0000 (пустая ссылка). Если указателю присвоено
значение Nil, то этот указатель ни на какие данные не ссылается.
Указатели могут сравниваться с помощью операций отношения = или <> (не равно).
Сравнение для указателей – ненадежная операция. Если два указателя содержат один и тот
же адрес в памяти, но записанный в них разными способами, то они считаются различными.
Зато можно проверить, ссылается ли указатель р на что–нибудь или нет путем сравнения p
<> Nil.
Содержимое ячейки доступно через имя указателя. Чтобы обратиться к данным,
находящимся по адресу, содержащемуся в указателе, используется символ “^”, который
ставится сразу после имени ссылочной переменной. Эта операция называется операцией
разыменования. Суть ее состоит в переходе от ссылочной переменной к значению, на
которое она указывает. Пусть имеется следующее описание:
Var a, b: ^Real;
тогда в программе с переменными а^ и b^ допустимы все действия, что и с любыми
переменными типа Real, например,
a^ := b^;
b^ := sin(a^); и т.д.
Память под любую динамически размещаемую переменную выделяется процедурой
New(p). Только после выполнения процедуры New имеет смысл обращаться к ссылочным
переменным. Параметром обращения к этой процедуре является типизированный
указатель. В результате обращения указатель приобретает значение, соответствующее
адресу, начиная с которого, можно разместить данные. Это адрес динамической области
данных или кучи. Начало кучи хранится в стандартной переменной Heaporg, конец – в
переменной Heapend. Текущую границу незанятой динамической памяти указывает
указатель Heapptr. Если Var i^ integer, то после выполнения New(i) указатель i приобретет
значение, которое перед этим имел указатель кучи Heapptr, а сам Heapptr увеличит свое
значение на 2, так как длина внутреннего представления типа Integer, с которым связан
указатель i, составляет 2 байта.