Динамическая память
Все переменные, объявленные в программе, размещаются в одной непрерывной области оперативной памяти, которая называется сегментом данных. Длина сегмента данных определяется архитектурой микропроцессоров 80x86 и составляет 65536 байт, что может вызвать затруднения при обработке больших массивов данных. С другой стороны, объем памяти персонального компьютера достаточен для успешного решения задач с большой размерностью данных. Выходом из положения может служить использование так называемой динамической памяти.
Динамическая память – это оперативная память персонального компьютера, предоставляемая программе при ее работе, за вычетом сегмента данных (64 Кбайт), стека (обычно 16 Кбайт) и собственно тела программы. По умолчанию размер динамической памяти определяется всей доступной памятью компьютера.
Динамическая память используется для обработки массивов данных большой размерности. Многие практические задачи трудно или невозможно решить без использования динамической памяти. Динамическое размещение данных означает использование динамической памяти непосредственно при работе программы. В отличие от этого, статическое размещение осуществляется компилятором Turbo Pascal в процессе компиляции программы. При динамическом размещении заранее не известны ни тип, ни количество размещаемых данных, к ним нельзя обращаться по именам, как к статическим переменным.
Адреса и указатели
Оперативная память представляет собой совокупность элементарных ячеек для хранения информации – байтов, каждый из которых имеет собственный номер. Эти номера называются адресами, они позволяют обращаться к любому байту памяти.
Для управления динамической памятью используются так называемые указатели. Указатель – это переменная, которая в качестве своего значения содержит адрес байта памяти. Адреса задаются совокупностью двух шестнадцатиразрядных слов, которые называются сегментом и смещением. Сегмент – это участок памяти, имеющий длину 65536 байт (64 Кбайт) и начинающийся с физического адреса, кратного 16 (т.е. 0, 16, 32, 48 и т.д.). Смещение указывает, сколько байт от начала сегмента необходимо пропустить, чтобы обратиться к нужному адресу.
Адресное пространство компьютера составляет 1 Мбайт. Для адресации в пределах 1 Мбайта нужно 20 двоичных разрядов, которые получаются из двух шестнадцатиразрядных слов (сегмента и смещения) следующим образом: содержимое сегмента смещается влево на 4 разряда, освободившиеся правые разряды заполняются нулями, результат складывается с содержимым смещения.
Физический адрес= Сегмент +Смещение
Фрагмент памяти в 16 байт называется параграфом, поэтому можно сказать, что сегмент адресует память с точностью до параграфа, а смещение – с точностью до байта. Каждому сегменту соответствует непрерывная и отдельно адресуемая область памяти. Сегменты могут следовать в памяти один за другим без промежутков или с некоторым интервалом, или перекрывать друг друга.
Таким образом, любой указатель представляет собой совокупность двух слов (данных типа Word), трактуемых как сегмент и смещение. С помощью указателей можно размещать в динамической памяти любые типы данных.
Объявление указателей
Как правило указатели связываются с некоторым типом данных. Такие указатели называются типизированными. Для объявления типизированного указателя используется значок ^, который помещается перед соответствующим типом.
Формат объявления типизированных указателей:
Var
идентификатор:^тип_данных;
Пример:
Var
P:^Integer; {объявлен указатель на данные целого типа}
G:^Real; {объявлен указатель на данные вещественного типа}
В Turbo Pascal можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. Для этого служит стандартный тип Pointer:
Var
идентификатор: Pointer;
Указатели такого рода называются нетипизированными. С их помощью удобно динамически размещать данные, структура и тип которых меняются в ходе работы программы.
Значениями указателей являются адреса переменных в памяти. Можно передавать значения только между указателями, связанными с одним и тем же типом данных. Если, например, объявлены указатели
Var
p1,p2 :^Integer;
p3 :^Real;
pp :Pointer;
то присваивание
p1:=p2;
вполне допустимо, в то время как
р1:= р3;
запрещено, поскольку р1и р3указывают на разные типы данных. Это ограничение, однако, не распространяется на нетипизированные указатели, поэтому можно записать
pp:=p3;
p1:=pp;
Обращение к значениям переменных заданного типа осуществляется следующим образом:
идентификатор^
Пример:
Var
p:^Integer;
Begin
...
{будет распечатан шестнадцатеричный адрес переменной}
WriteLn(p);
{будет распечатано значение переменной по заданному адресу}
WriteLn(p^);
...
End;
Структура динамической памяти
Вся динамическая память в Turbo Pascal рассматривается как сплошной массив байтов, который называется кучей. Физически куча располагается в старших адресах сразу за областью памяти, которую занимает тело программы. Начало кучи хранится в стандартной переменной HeapOrg, конец — в переменной HeapEnd. Текущую границу незанятой динамической памяти указывает указатель HeapPtr.
Расположение кучи в памяти компьютера
Все операции с кучей выполняются под управлением особой подпрограммы, которая называется администратором кучи. Она автоматически пристыковывается к программе компоновщиком Turbo Pascal и ведет учет всех свободных фрагментов в куче.
Администратор кучи – это служебная подпрограмма, которая обеспечивает взаимодействие пользовательской программы с кучей. Администратор кучи обрабатывает запросы процедур работы с динамической памятью и изменяет значения указателей HeapPtrи FreeList. Указатель HeapPtrсодержит адрес нижней границы свободной части кучи, а указатель FreeList — адрес описателя первого свободного блока. В модуле Systemуказатель FreeList описан как Pointer, однако фактически он указывает на следующую структуру данных:
Type
PFreeRec=^TFreeRec;
TFreeRec=record
Next: Pointer;
Size : Pointer
end;
Эта списочная структура предназначена для описания всех свободных блоков памяти, которые расположены ниже границы HeapPtr. Происхождение блоков связано с «ячеистой» структурой кучи. Поле Nextв записи TFreeRecсодержит адрес описателя следующего по списку свободного блока кучи или адрес, совпадающий с HeapEnd, если этот участок последний в списке. Поле Sizeсодержит ненормализованную длину свободного блока или 0, если ниже адреса, содержащегося в HeapPtr нет свободных блоков. Ненормализованная длина определяется так: в старшем слове этого поля содержится количество свободных параграфов, а в младшем – количество свободных байт в диапазоне 0...15.
Сразу после загрузки программы указатели HeapPtrи FreeListсодержат один и тот же адрес, который совпадает с началом кучи (этот адрес содержится в указателе HeapOrg). При этом в первых 8 байтах кучи хранится запись, соответствующая типу TFreeRec(поле Nextсодержит адрес, совпадающий со значением HeapEnd, а поле Size – ноль, что служит дополнительным признаком отсутствия «ячеек» в динамической памяти). При работе с кучей указатели HeapPtr и FreeList будут иметь одинаковые значения до тех пор, пока в куче не образуется хотя бы один свободный блок ниже границы, содержащейся в указателе HeapPtr. Как только это произойдет, указатель FreeList станет ссылаться на начало этого блока, а в первых 8 байтах освобожденного участка памяти будет размещена запись TFreeRec. Используя FreeList как начало списка, программа пользователя всегда сможет просмотреть весь список свободных блоков и при необходимости модифицировать его.
Процедуры и функции для работы с динамической памятью
ADDR(x) –функция возвращает результат типа Pointer, в. котором содержится адрес аргумента. Здесь x – любой объект программы (имя любой переменной, процедуры, функции). Возвращаемый адрес совместим с указателем любого типа. Аналогичный результат возвращает операция @.
CSEG – функция возвращает значение, хранящееся в регистре CSмикропроцессора (в начале работы программы в регистре CSсодержится сегмент начала кода программы). Результат возвращается в слове типа Word.
Dispose(типизированный_указатель) – процедура возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за типизированным указателем. При повторном использовании процедуры применительно к уже освобожденному фрагменту возникает ошибка периода исполнения.
DSEG функция возвращает значение, хранящееся в регистре DS микропроцессора (в начале работы программы в регистре DSсодержится сегмент начала данных программы). Результат возвращается в слове типа Word.
FreeMem(нетипизированный_указатель,size) – процедура возвращает в кучу фрагмент динамической памяти, который ранее был зарезервирован за нетипизированным указателем. Здесь size — длина в байтах освобождаемого фрагмента.
GetMem(нетипизированный_указатель,size) – процедура резервирует за нетипизированным указателем фрагмент динамической памяти требуемого размера.
Mark(PTR) – процедура запоминает текущее значение указателя кучи HeapPtr. Здесь PTR — указатель любого типа, в котором будет возвращено текущее значение HeapPtr. Используется совместно с процедурой Releaseдля освобождения части кучи.
MaxAvail – функция возвращает размер в байтах наибольшего непрерывного участка кучи. Результат имеет тип LongInt.
MemAvail – функция возвращает размер в байтах общего свободного пространства кучи. Результат имеет тип LongInt.
New(типизированный_указатель) – процедура резервирует фрагмент кучи для размещения переменной. Процедура New может вызываться как функция. В этом случае параметром обращения к ней является тип переменной, размещаемой в куче, а функция New возвращает значение типа указатель. Пример:
Type
Pint=^Integer;
Var
p: Pint;
Begin
p:=New(Pint);
End.
QFS(x) – функция возвращает значение типа WORD, содержащее смещение адреса указанного объекта. Здесь x – выражение любого типа или имя процедуры.
PTR(SEG,OFS) – функция возвращает значение типа Pointerпо заданному сегменту SEGи смещению OFS. Здесь SEG – выражение типа Word, содержащее сегмент; OFS— выражение типа WORD, содержащее смещение. Значение, возвращаемое функцией, совместимо с указателем любого типа.
RELEASE (PTR) – процедура освобождает участок кучи. Здесь PTR – указатель любого типа, в котором предварительно было сохранено процедурой Markзначение указателя кучи. Освобождается участок кучи от адреса, хранящегося в PTR, до конца кучи. Одновременно уничтожается список всех свободных фрагментов, которые, возможно, были созданы процедурами Disposeили FreeMem.
SEG(x) – функция возвращает значение типа Word, содержащее сегмент адреса указанного объекта. Здесь x – выражение любого типа или имя процедуры.
SizeOf(x) – функция возвращает длину в байтах внутреннего представления указанного объекта. Здесь x – имя переменной, функции или типа.
Дата добавления: 2015-04-15; просмотров: 793;