Управление динамической памятью.
Определяемые в примерах предыдущего параграфа указатели для наглядности содержали адреса статически размещенных переменных. Однако основное назначение указателей - адресация динамических переменных. Такие переменные располагаются в свободной области, называемой динамической памятью или «кучей». Эта область расположена после программы, и ее объем составляет около 200 ... 300 кБ, как это представлено на рис. 5.
Свободная память
HeapOrg
HeapPtr
HeapEnd
Рис.5. Размещение динамической области.
(Соответственно, чем больше объем программы, тем меньше размер свободной области памяти.) На этом рисунке также показаны значения стандартных переменных Borland Pascal, используемых для управления динамической областью:
HeapOrg - указатель на начало динамической области;
HeapEnd - указатель на конец динамической области;
HeapPtr - указатель на текущее значение границы
свободной динамической области
Заказать и освободить память требуемого объема из динамической области можно используя специальные процедуры и функции.
1. Процедура New (Var <типизированный указатель>) - возвращает адрес выделенного участка памяти через параметр-переменную. Размер участка памяти определяется базовым типом указателя.
Например:
Var pi: ^integer; ...
New(pi); {теперь pi содержит адрес двух байт, выделенных из
динамической памяти под размещение переменной целого типа}
2. Функция New (<тип типизированного указателя>):роinter - возвращает адрес выделенного участка памяти. Размер участка памяти также определяется базовым типом указателя.
Например:
Type tpi: Integer;
Var pi:tpi; ...
pi:= New(tpi); {pi также содержит адрес двух байт, выделенных
из динамической памяти под размещение переменной
целого типа}
Для размещения изученных нами типов переменных можно использовать как процедуру New, так и функцию New.
3. Процедура Dispose (<типизированный указатель>) - освобождает память по адресу, хранящемуся в указателе.
Например:
Dispose (pi);...
При попытке применить процедуру к указателю, имеющему значение nil, или освободить уже освобожденную память фиксируется ошибка и выдается соответствующее сообщение.
Серия последовательных обращений к New и Dispose обычно приводит к фрагментации памяти - память разбивается на небольшие фрагменты с чередованием свободных и занятых участков. В результате может возникнуть ситуация: свободной памяти для размещения новой переменной достаточно, но она не может быть размещена из-за отсутствия непрерывного участка требуемого размера.
Для уменьшения явления фрагментации используют специальные процедуры.
4. Процедура Mark (Var p:pointer) - запоминает значение HeapPtr в указателе р, полученном в качестве параметра.
5. Процедура Release (Var p:pointer) -освобождает весь фрагмент памяти, начиная с адреса р, зафиксированного в указателе процедуры Mark.
Например:
new(pl);
new(p2);
mark(p);
new(p3);
new(p4);
release(p);...
Совместное использование процедур Dispose и Release недопустимо, так как Release разрушает список освобожденных фрагментов, создаваемый при выполнении Dispose.
Динамическую память можно выделять фрагментами, указывая их размер.
1. Процедура GetMem (Var p:pointer; size:word) - запрашивает у системы память размера, указанного в параметре size (запрашиваемый объем не должен превышать 64КБ) и помещает адрес выделенного системой фрагмента в переменную типа pointer с имеем р. Как правило, данный способ выделения памяти используется, если требуется память под размещение буферов, формат которых программисту не известен.
2. Функция SizeOflx): word - возвращает длину указанного объекта х в байтах.
3. Процедура FreeMem (p:pointer; size:word)- освобождает область памяти, выделенную процедурой GetMem.
Однако каким бы способом не запрашивалась память, может возникнуть ситуация, когда оставшаяся свободная память меньше требуемой, и память выделить невозможно. В этом случае система по умолчанию выдает сообщение об ошибке выполнения и аварийно завершает задачу.
Избежать подобной ситуации можно несколькими способами.
Первый способ заключается в предварительной проверке наличия свободной памяти требуемого размера.
9. Функция Maxavail: longint - возвращает длину максимального непрерывного участка памяти.
10. Функция Memavail: longint - возвращает размер всей свободной памяти - сумму длин всех свободных фрагментов.
Второй способ базируется на возможности перехвата системной обработки ошибки выделения памяти. Для этого необходимо определить свою подпрограмму обработки ошибки, в которой вместо признака ошибки распределения динамической памяти 0, установленного по умолчанию, необходимо задать HeapFunc:=l, например:
Function HeapFunc(size: word) : integer; far;
begin
HeapFunc: =1;
end;
В программе необходимо определить адрес подпрограммы обработки ошибки HeapError, указав собственную программу HeapFunc:
HeapError:=@HeapFunc; ...
Использование такой подпрограммы приведет к тому, что процедуры New и GetMem при исчерпании памяти вернут указатели, установленные в nil, и выполнение программы не будет прервано. Действия по обработке возникшей ситуации выполняет программист, который должен проверить указатели после возврата из процедур выделения памяти.
Лекция 22. Связанные динамические данные (2 часа)
Дата добавления: 2015-12-01; просмотров: 828;