Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МУ к пр з №1 и 2 прог ч2 2011.doc
Скачиваний:
4
Добавлен:
23.11.2018
Размер:
264.19 Кб
Скачать

Dispose(pe); {освобождает 10 байтов в куче}

Более подробно с набором подпрограмм для работы с динамической памятью, а также с правилами работы с указателями можно ознакомиться в [1].

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

Широкое распространение среди односвязных списков получили такие динамические структуры данных, как стеки, очереди и кольцеобразные списки.. Стек организован таким образом, что программе доступна лишь его вершина: оттуда можно взять элемент или записать его туда. Таким образом, элементы можно извлечь из стека в порядке, обратном порядку их записи -"последний вошел - первый вышел". Очередь же реализует другой вариант доступа к данным - "первый вошел - первый вышел’. Новый элемент добавляется в конец очереди, а выбирается первый. Если последний элемент очереди связать с первым элементом, то получится замкнутый, кольцеобразный список..

Для работы с такими структурами при выполнении индивидуального задания рекомендуется использовать базовые процедуры:

Init - создать список;

Clear - очистить список;

PutData - поместить элемент в список ;

GetData - взять элемент из списка;

LookData - просмотр элементов списка.

Рассмотрим реализацию этих процедур:

Type

Ukaz = ^TStack;

TStack = record

Inf: Integer;

Next: Ukaz;

End;

Procedure Init (var Top: Ukaz

Begin

Top:=Nil;

End;

Procedure Clear (var Top: Ukaz);

Var

Kon: Ukaz;

Begin

While Top<>Nil Do

Begin

Kon:=Top;

Top:=Top^.Next;

Dispose(Kon);

End;

End;

procedure PutData(var S: Ukaz; Data: Integer);

var

P: Ukaz;

begin

New(P);

P^.Inf:=Data;

P^.Next:=S;

S:=P;

end;

procedure GetData(var S: Ukaz; Data: Integer);

var

P:Ukaz;

begin

Data:=S^.Inf;

P:=S;

S:=S^.Next;

S^.Inf:=Data;

Dispose(P);

end;

procedure LookData (var Top: Ukaz);

Var

Kon: Ukaz;

Begin

While Top<>Nil Do

Begin

Writeln(Top^.Inf);

Kon:=Top;

Top:=Top^.Next;

End;

End;

Следует обратить особое внимание на следующие детали, связанные с выделением и освобождением динамической области через вызовы стандартных процедур New и Getmem, Dispose, Mark и Release. Если в системе нет памяти для кучи, равной минимальному значению, программа не будет выполняться.

Куча имеет стековую структуру, растущую от нижних адресов в сегменте кучи. Нижняя граница кучи хранится в переменной HeapOrg, а вершина - в HeapPtr. Когда динамическая переменная размещается в куче так назывыемый администратор кучи передвигает HeapPtr вверх на размер этой переменной. Максимальный размер переменной, который может быть распределен в куче, равен 65519 байт, поскольку каждая переменная должна полностью находиться в одном сегменте.

Динамическую переменную легче всего удалить с помощью процедур Mark и Release. В фрагменте программы

New (Ptr1);

Mark (P);

New (Ptr2);

New (Ptr3);

процедура Mark (P) помечает состояние кучи перед распределением переменной Ptr2:

Если выполнить оператор Release (P), то состояние кучи станет как на рисунке ниже:

Выполнение операции Release (HeapOrg) полностью очищает всю кучу, так как HeapOrg - нижняя граница кучи.

Для программ, которые освобождают указатели в порядке, обратном их распределению, процедуры Mark и Release очень эффективны. При случайном распределении и освобождении динамической области памяти используют процедуры Dispose и FreeMem (например, Dispose (Ptr3)- освобождает блок содержимого Ptr3, т.е. образовывает свободное пространство) позволяют программе освобождать динамические переменные в любое время. При этом “куча” становится фрагментированной (“c дырками”), свободные блоки памяти, создаваемые и разрушаемые в этом режиме, фиксируются для последующего использования.

Существует, так называемый, список свободных блоков, который растет сверху вниз от верхней границы сегмента кучи.

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

Необходимо помнить, что процедура Release всегда очищает список свободных блоков, т.е. администратор “кучи” забывает все свободные блоки ниже указателя кучи. При смешивании вызовов Mark и Release с вызовами Dispose и FreeMeì не должно быть свободных блоков.

Переменная FreeList из стандартного модуля System указывает на первый свободный блок в куче. Этот блок содержит указатель на следующий свободный блок и т.д. Последний свободный блок содержит указатель на вершину кучи (т.е. на положение, указываемое HeapPtr. Если нет свободных блоков, то FreeList равна HeapPtr. Размер каждого блока округляется с избытком до величины, кратной 8. Например, блок в 50 байт будет округлен до 56 байт и при следующем запросе на распределение, например, от 49 до 55 байт будет полностью использовать этот блок (56 байт), не оставляя от 7 до 1 байт свободных, которые могли бы фрагментировать кучу.

Когда администратор кучи не может обработать запрос на распределение памяти, то вызывается функция обработки ошибок кучи HeapFunc:

Function HeapFunc (Size:word):Integer; far;

Функция обработки устанавливается присваиванием ее адреса переменной HeapFrror:

HeapFrror := @HeapFunc.

Функция HeapFunc возвращает 0,1 или 2.

  • 0- возникает ошибка времени выполнения;

  • 1- программы New или GETMEM возвращают указатель, равный Nil без аварийного завершения программы:

  • 2- успешное распределение памяти.

Для многих программ будет удобнее следующая функция обработки ошибок

Function HeapFunc (Size:word):Integer; bar;

begin

HeapFunc:=1;

end;

Когда эта функция установлена, New и GetMem будет возвращать Nil при невозможности распределить память, не приводя к аварийному завершению программы, что позволяет провести анализ состояния следующим образом:

HeapFrror := @HeapFunc{перехват функции контроля состояния кучи}

GetMem(p,size);{выделить Size байт в динамической области}

If p=Nil Then

Begin

writeln(‘Ошибка кучи’);

Release(p1);{освобождение Heap с адреса Р1, заранее помеченного Mark(p1)}

GetMem(p1,size);{повторение выделения нужного размера области с адреса р1}

p:=p1;

End;