Читайте также:
|
|
Процедура New(Var P: Pointer) отводит место для динамической переменной и присваивает ее адрес ссылке Р. Для того чтобы эта процедура выделила необходимое место для такой переменной, в разделе описания var, необходимо указать тип (фактический размер) переменной (стандартный или пользовательский). С этой целью слева от идентификатора типа ставится знак карата «^». Этот знак фактически указывает размер переменной, которую порождает процедура New. Последующее использование этой же процедуры позволит выделить место для другой динамической переменной, адрес первой ячейки которой будет сдвинут относительно адреса первой ячейки первоначальной переменной на то количество ячеек (байт), которые требуются для размещения первоначальной переменной.
Процедура Dispose(Var P:Pointer) уничтожает связь, созданную ранее процедурой New, между указателем Р и объектом, на который он указывает (to dispose — освобождать).Обычно глубокое понимание «устройства» динамических переменных и усвоение правил работы с ними приходит далеко не сразу, и поэтому к некоторым абзацам настоящего раздела вам придется возвращаться несколько раз.
Следующий фрагмент программы поясняет использование упомянутых процедур:
Туре
Okrygn_Zap= Record
x:Integer; {Координаты центра окружности}
y:Byte;
Radius: Byte;
End;
Ykazat_Zap= ^Okrygn_Zap;
Var
Ykazat_l:^Char;{Указатель на элемент типа Char}
Ykazat_2:^Real;{Указатель на элемент типа Real}
Ykazat_3:^Okrygn_Zap;{Указатель на элемент типа Okrygn_Zap}
BEGIN
New(Ykazat_l);{Создали динамическую переменную}
Ykazat_1^:= 'A';{Присваивание объекту конкретного значения}
WriteLn('Литера',Ykazat_1^);{Литера А}
Dispose(Ykazat_1); {Освобождает память, занятую переменной Ykazat_l}
END.
В разделе описания переменных место под динамические переменные еще не выделяется, а компилятору лишь сообщается информация о том, что в теле программы переменная заданного типа (размера) может быть выделена. В теле программы процедура New выделяет в куче необходимое количество смежных ячеек под заданную динамическую переменную (в примере это Ykazat_l) и адрес первой ячейки этого блока ячеек записывает в ячейках статической памяти, присваивая им имя динамической переменной. Таким образом, динамическая переменная состоит из двух частей: статической переменной-адреса (имя переменной) и объекта, расположенного в куче по указанному адресу.
Для обращения к такому объекту знак карата следует ставить после идентификатора динамической переменной. Процедура Dispose(Ykazat_l) возвращает состояние кучи, которое было в ней до использования процедуры New(Ykazat_l). Ячейки объекта переменной Ykazat_l могут теперь быть использованы для размещения в куче других объявленных переменных: Ykazat_2, Ykazat_3.
Очень наглядно обе части динамической переменной представлять в виде прямоугольников, соединенных стрелкой. Такая модель динамической переменной приведена на рис. 3.
В первом прямоугольнике «хранится» адрес, а во втором — объект динамической переменной. Стрелка как бы связывает конкретные ячейки статической памяти, в которых указан адрес объекта, с ячейками памяти в куче, выделенные под объект по указанному адресу.
Рис. 3. Модель динамической переменной
Прокомментируем работу следующего фрагмента программы.
Var
A,B:^Integer;
{B программе могут быть использованы динамические переменные типа Integer}
BEGIN
New(A); New(B);
{Динамические переменные А и В порождены: места под объекты А^ и В^
в куче выделены, а их адреса записаны в переменных А и В. Сами же объекты
еще пусты, в них ничего не записано}
А^:= 19; В^:= 49;
{В объекты А^ и В^ динамических переменных А и В записали числа
соответственно 19 и 49}
А:= В;
{В динамических переменных А и В записан адрес одного и того же объекта,
в котором расположено число 49. Адреса объектов обеих переменных совпадают.
Объект с числом 19 теперь является недоступным для программиста}
В:= Nil;
{Динамическая переменная В не связана с каким-либо объектом}
END.
Если указатели однотипны, как в вышеприведенном примере переменные А и В, то их значения можно присваивать друг другу (А:= В;). Для таких указателей допустимы операции сравнения (А> В).
Если же типы указателей различны, то присваивать их значения друг другу нельзя. Это ограничение реализовано в синтаксисе языка: за его выполнением строго следит компилятор. Поэтому, если даже размеры разных типов одинаковы (например, byte и char), то компилятор все равно взаимные присваивания между ними не позволит.
Однако в Turbo Pascal имеется специальный механизм, который позволяет вес же обойти это ограничение. Он основывается на использовании нетипизированных указателей pointer. Такие указатели объявляются стандартным образом:
Var
х: Pointer;
Нетипизированному указателю может быть присвоено значение любого типизированного указателя либо наоборот. Очевидно поэтому, что между нетипизированными указателями допустимы взаимные присваивания, как и для любых однотипных переменных. Нетипизированных указатель представляет собой своеобразный буфер, выделенный в сегменте данных для хранения адреса динамической переменной любого типа. Типизированному и Нетипизированному указателям может назначаться либо адрес, либо Nil.
Идентификатор процедуры New может также использоваться в качестве функции, возвращающей указатель заданной переменной
if B= Nil then B:= New(A);
Ранее отмечалось, что процедура Dispose возвращает состояние кучи, которое было в ней до использования процедуры New, и ячейки, ранее занятые под уничтоженную переменную, могут быть использованы для размещения в куче любых других объявленных переменных. Однако это утверждение не совсем верно. Дело в том, что в освободившемся блоке ячеек могут быть размещены только объекты тех переменных, размеры которых либо равны числу освободившихся ячеек, либо меньше этого числа. В первом случае куча заполняется информацией снизу вверх без пропусков ячеек памяти. Во втором случае, когда размер освободившегося блока памяти больше размера объекта новой динамической переменной, блок ячеек заполняется лишь частично. Конечно, он может быть дополнительно заполнен введением последующей динамической переменной, если ее размер не будет превышать этот остаток. Однако во всех случаях, когда под новую переменную необходим больший блок ячеек, такой блок выделяется сразу же за блоком памяти объекта последней динамической переменной, если даже он совсем пуст или же заполнен лишь частично.
В связи с этим может возникнуть ситуация, при которой программа после ряда последовательных порождений и уничтожений динамических переменных не сможет разместить в куче очередную переменную, размер которой будет превышать размер всех выделяемых и освобождаемых до этого блоков. Таким образом, куча будет фактически пустой, но из-за ее фрагментации в ней уже невозможно будет поместить новую переменную.
Конечно, такая ситуация может возникнуть только в отдельных программах. Но само наличие такой возможности является недостатком процедуры New, и это следует принимать во внимание при программировании. Однако в Turbo Pascal имеется эффективный способ управления кучей, в котором этот недостаток отсутствует.
Дата добавления: 2015-07-11; просмотров: 303 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Распределение оперативной памяти | | | Управление блоками динамической памяти |