Создание адреса функцией Ptr




Функция Ptr(Seg, Ofs: Word) выполняет противоположную функции Addr работу: организует ссылку на место в памяти, определяемое заданными сегментом и смещением. Необходимость в такой функции возникает всякий раз, когда требуется наложить динамическую структуру на системную область памяти (системные, т.е. зарезервированные, области памяти достаточно жестко фиксированы, и их адреса описаны в специальной литературе). Если, например, известно, что образ текстового экрана начинается с адреса $В000: $0000 и занимает 4000 байт (цветной и черно-белый режимы, 80 столбцов на 25 строк), то можно «наложить» на него структуру, например массив, используя ссылку на такой массив и функцию Ptr (рис. 10.2):

TYPE VideoArray = Array [0..3999] of Byte; VAR V: ^VideoArray; { ссылка на структуру } BEGIN V:= Ptr($B000, 0); { Далее V^[i] обращается непосредственно к ячейкам видеопамяти в текстовом режиме } ... END.

Рис. 10.2

Обращаем внимание на отсутствие вызовов New и Dispose. Они не нужны, так как массив буквально накладывается, а не создается вновь.

Так как значение типа Pointer, возвращаемое функцией Ptr, совместимо со всеми ссылочными типами, можно наложить любую структуру на какой угодно участок памяти. Кроме того, его разрешается разыменовывать, т.е. записывать конструкции вида

Ptr($40, $40)^ {192}

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

B:= Byte(Ptr($40, $40)^); {B — значения байта $40:$40 }

W:= Word(Ptr($40,$40)^); { W — значение слова $40:$40 }

X:= VideoArray(Ptr($B800,0)^); { X — статический массив типа VideoArray содержит теперь в себе образ видеопамяти (текстовое изображение) }

и т.д.

Не стоит только приводить к типу String и производным от него: никогда не известно, что окажется в нулевом элементе строки, где должен храниться ее реальный размер.

Определение размеров типов и переменных

Функция SizeOf (X): Word возвращает объем в байтах, занимаемый X. Причем X может быть не только переменной, но также и идентификатором типа (рис. 10.3).

TYPE ХТуре = Array [1..10, 1..10] of Byte; CONST L: Longint = 123456; VAR X: String; BEGIN WriteLn(SizeOf(Xtype): 10, SizeOf(L): 10, SizeOf(X)) END.

Рис. 10.3

Значение SizeOf(строка) всегда дает максимальное значение длины строки. Реальное значение дает функция Length.

Вообще говоря, функцию SizeOf можно рассматривать как макроподстановку размеров типов и переменных, вычисляемых на этапе компиляции.

Применительно к данным типа «объект» (OBJECT) эта функция должна использоваться более осторожно, так как у объектов может не быть заранее предопределяемого размера. Обсуждение этого можно найти в гл. 13. {193}

Глава 11. Ссылки, динамические переменные и структуры

Динамическими структурами данных считаются такие, размер которых в процессе работы программы заранее не известен или изменяется и (или) для которых место в памяти ПЭВМ отводится во время выполнения программы. Необходимость в динамических структурах данных обычно возникает в следующих случаях:

1. Используются переменные, имеющие довольно большой размер, необходимые в одних частях программы и совершенно не нужные в других, т.е. переменные, освобождающие память после их использования.

2. В процессе работы программы нужен массив или иная структура, размер которой изменяется в широких пределах и труднопредсказуем.

Кроме этих двух случаев, общих для различных версий Паскаля, при программировании на Турбо Паскале есть еще один. А именно: когда размер переменной (массива или записи) превышает 64K.

Во всех этих случаях возникающие проблемы можно решить, применяя динамические переменные и ссылочные типы данных.

Ссылочные переменные

Основным механизмом для организации динамических данных является выделение в специальной области памяти, называемой «кучей», непрерывного участка (блока) подходящего размера и сохранения адреса начала этого участка в специальной переменной. Такие переменные называют ссылочными переменными или просто ссылками (reference). Часто используется синоним этого термина — «указатель» (pointer), но в Турбо Паскале это название имеет особый смысл.

В Турбо Паскале для определения ссылочной переменной нужно описать ее как переменную, имеющую ссылочный тип. В качестве ссылочного можно использовать встроенный тип Pointer или любой другой тип, определенный пользователем следующим образом:

TYPE

ИмяСсылочногоТипа = ^ИмяБазовогоТипа;

где ИмяБазовогоТипа — любой идентификатор типа. В результате {194} этого определения создаваемые затем ссылочные переменные будут указывать на объекты базового типа, определяя тем самым динамические переменные базового типа. Например:

TYPE { БАЗОВЫЕ ТИПЫ}

DimType = Array [1..10000] of Real; { массив }
RecType = RECORD { запись }

...

END;

ObjType = OBJECT { объект }

...

END;

{ ССЫЛОЧНЫЕ ТИПЫ}

IntPtr = ^Integer; { ссылка на целое значение }

DimPtr = ^DimType; { ссылка на массив данных }

RecPtr = ^RecType; { ссылка на запись }

ObjPtr = ^ObjТуре; { ссылка на объект }
XXXPtr = Pointer; { ссылка "вообще" — указатель }

Условимся называть в дальнейшем указателем, а не ссылкой те переменные, которые имеют обобщенный тип Pointer. Этот тип совместим со всеми прочими ссылочными типами.

J адрес хранения значения “123”
——→
 
Адрес J отличается от адреса значения

Все ссылочные переменные имеют одинаковый размер, равный 4 байтам, и содержат адрес начала участка оперативной памяти, в котором размещена динамическая структура данных. Отношение между ссылочной переменной и объектом, на который она указывает, наглядно представлено на рис. 11.1. Здесь J — ссылочная переменная, указывающая на значение целого типа (VAR J: ^Integer). Сравните с простой переменной I типа Integer на том же рисунке.

 

 

I=123

Адрес I является адресом значения

Рис. 11.1

Чтобы ссылка ни на что не указывала, ей присваивается значение nil, например:

J:= nil;

Это предопределенная константа типа Pointer, соответствующая адресу 0000:0000. {195}

Операция разыменования

Основной операцией при работе со ссылочными переменными является операция разыменования. Суть ее состоит в переходе от ссылочной переменной к значению, на которое она указывает. Эта операция обозначается указанием символа «^» следом за ссылочной переменной. Результатом операции является значение объекта, на который указывала ссылочная переменная, или, что то же самое, динамическая переменная. Так, пусть мы имеем две ссылочные переменные I и J, указывающие на объекты целого типа, значения которых равны 2 (I^) и 4 (J^) соответственно. Для того чтобы скопировать содержимое переменной I^ в переменную J^, необходимо выполнить оператор

J^:= I^;

Следует отметить, что нужно писать именно I^ и J^, поскольку оператор вида

J:= I;

приведет к копированию адреса значения, на которое указывает I, в ссылочную переменную J. В этом случае мы получим две ссылки на одно и то же значение. Значение, на которое раньше указывала переменная J, будет потеряно. На рис. 11.2 показан результат выполнения этих двух операторов (а — ситуация до (слева) и после выполнения оператора J^:=I^; б — ситуация до (слева) и после выполнения оператора J:=I). После выполнения оператора J:=I ссылка на значение 4 теряется (рис. 11.2, б, справа), и к нему больше нет доступа.

I
——→
 
I
——→
 

 

 

J

J^:= I^;

J
——→
 
——→
 

 

а)

 

I
——→
 
J
——→
 
I
——→
 

 

J:= I;
J
———————
 

 

б)

Рис. 11.2 {196}

Само значение теперь будет просто пассивно занимать память, т.е. превратится в «мусор».

Ссылочные переменные и указатели совместимы между собой по типу, т.е. нет ошибки в присваивании

DimPtr:= RecPtr;

но после разыменования контроль типов становится строгим и

DimPtr^:= RecPtr^;

дает ошибку. Здесь речь идет уже не о значениях вполне совместимых адресов, а о разнотипных значениях по этим адресам.

Ссылки могут сравниваться между собой. Под этим понимается сравнение соответствующих сегментов и смещений адресов, хранимых в них. Имеют смысл лишь проверки на равенство и на неравенство ссылок или указателей:

if DimPtr = XXXPtr then...;

if DimPtr <> XXXPtr then...;

Сравнения ссылок не всегда работают корректно. Если две ссылки указывают на один и тот же адрес в памяти, но этот адрес записан в них различными значениями (что вполне возможно), то они считаются различными. Процедуры New и GetMem всегда возвращают ссылки, приведенные к такому виду, что смещения адреса имеют значения от 0 до 15 ($F), и они будут сравниваться корректно. А специальные функции типа Addr, Ptr этого не делают, и сравнивать их результаты с чем-либо надо с большой осторожностью.

Разыменованные ссылки на структуры индексируются (массивы) или разделяются на поля (записи, объекты) обычным образом. Для определенных выше ссылок это выглядит следующим образом:

DimPtr^ [i] — доступ к элементу i динамического массива,

RecPtr^.Поле — доступ к полю динамической записи,

ObjPtr^.Метод — доступ к методу динамического объекта.

После объявления в программе ссылки или указателя его значение не определено и содержит случайный адрес. Для работы с динамическими переменными их всегда необходимо сначала разместить специальными процедурами в памяти или хотя бы присвоить ссылке корректное значение адреса.



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2022-11-01 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: