- •Лабораторная работа № 3 Разработка программы использования динамической памяти
- •Краткие теоретические сведения
- •Статические и динамические переменные
- •Указатели
- •Типизированные указатели
- •Нетипизированный указатель (pointer)
- •Доступ к переменной по указателю
- •Управление динамической памятью
- •Процедуры динамического распределения
- •Контрольные вопросы
- •Индивидуальные задания
Нетипизированный указатель (pointer)
В Турбо Паскале можно объявлять указатель и не связывать его при этом с каким-либо конкретным типом данных. Для этого служит стандартный тип Pointer. Он обозначает нетипизированный указатель, т. е. указатель, который не указывает ни на какой определенный тип. С помощью нетипизированных указателей удобно динамически размещать данные, структура и тип которых меняются в ходе программы.
Переменные типа Pointer не могут быть разименованы: указание символа ^ после такой переменной вызывает появление ошибки. Как и значение, обозначаемое словом Nil, значения типа Pointer совместимы со всеми другими типами указателей.
Над значениями ссылочных типов допускаются две операции сравнения на равенство и неравенство, например @Х <> @Y или Р1 = Р2.
Два указателя равнытолько в том случае,если они ссылаются на один и тот же объект.
Доступ к переменной по указателю
Для доступа к переменной имеются две возможности: первая - использовать идентификатор переменной, вторая - воспользоваться адресом этой переменной, который содержится в указателе на эту переменную.
Например, чтобы увеличить значение переменной I, на которую указывает указатель Р: по первому способу можно записать: I:=I+2.Для реализации второго, косвенного доступа к переменной по указателю, используется разыменование указателя. Правило разыменования таково: для того чтобы по указателю на переменную получить доступ к самой переменной, нужно после переменной-указателя поставить знак ^. Так, запись: Writeln(Int2^) означает напечатать значение переменной, на которую ссылается указатель Int2. Поэтому чтобы увеличить значение переменной I, на которую указывает указатель Р, используя косвенный доступ к переменной по указателю, можно записать: Р ^:= P^ + 2.
Разыменование допускается для любых ссылочных типов. В случае использования указателя на указатель возможно многократное разыменование. Разыменование считается некорректным, если ссылочная переменная имеет значение Nil, т. е. в этом случае не существует переменной, на которую ссылается указатель.
Управление динамической памятью
Вся динамическая память в Турбо Паскале рассматривается как сплошной массив байтов, который называется кучей. Физически куча располагается в старших адресах сразу за областью памяти, которую занимает тело программы.
Начало кучи хранится в стандартной переменной HeapOrg, конец - в переменной HeapEnd. Текущую границу незанятой динамической памяти указывает указатель HeapPtr.
Для управления кучей используются следующие стандартные процедуры и функции.
Процедуры динамического распределения
Dispose Уничтожает динамическую переменную
FreeMem Уничтожает динамическую переменную данного размера
GetMem Создает новую динамическую переменную заданного размера и устанавливает переменную-указатель для нее
Mark Записывает в переменной-указателе состояние кучи
New Создает новую динамическую переменную и устанавливает на нее переменную-указатель
Release Возвращает кучу в заданное состояние
Функции динамического распределения
MaxAvail Возвращает размер наибольшего непрерывного свободного блока кучи, соответствующей размеру наибольшей динамической переменной, которая может быть распределена в момент вызова MaxAvail
MemAvail Возвращает количество имеющихся в куче свободных байтов
Функции для работы с указателями и адресами
Addr Возвращает адрес заданного объекта
Cseg Возвращает текущее значение регистра CS
Dseg Возвращает текущее значение регистра DS
Ofs Возвращает смещение заданного объекта
Ptr Преобразует базовый адрес сегмента и смещение в значение типа указатель
Seg Возвращает сегмент для заданного объекта
SPtr Возвращает текущее значение регистра SP
Sseg Возвращает текущее значение регистра SS
Основные действия над динамическими переменными - создание и уничтожение - реализуются в Турбо Паскале стандартными процедурами NewиDispose.
Процедура New предназначена для создания динамических переменных определенного типа. Она отводит новую область памяти в куче для данной динамической переменной и сохраняет адрес этой области в переменной-указателя. Можно присвоить значение переменной-указателю и с помощью оператора @ или функции Ptr. Оператор @ устанавливает переменную-указатель на область памяти, содержащую существующую переменную, включая и те переменные, которые имеют идентификаторы. Функция Ptr устанавливает переменную-указатель на определенный адрес в памяти. Например:
New(Int1);
P1 := @X;
Ptr($40, $49);
Для освобождения памяти в куче предназначена процедура Dispose с параметром указатель на динамическую переменную, причем эта переменная должна быть ранее размещена в куче посредством New, а тип размещенной переменной должен совпадать с базовым типом параметра процедуры Dispose. Например: Dispose(Int1) освобождает выделенный в предыдущем примере в куче фрагмент памяти так, что его можно снова использовать, если потребуется.
Если в программе не предполагается использование кучи, то при работе с программой Турбо Паскаля рекомендуется снизить потребность в памяти для создаваемой программы с помощью меню Options/Heap.
При освобождении динамической памяти нужно соблюдать осторожность, т.е. пользоваться либо New/Dispose, либо New/Mark/Release, либо GetMem/FreeMem, но ни в коем случае не путать сочетание этих процедур.
Задания
1.Дан массив из 10 целых чисел. Присвоить указателю адрес начала размещения массива в памяти . Вывести на печать 1 5 и 10 элементы, на которые указывает указатель на массив.
2. Создать программу, которая создавала набор из 7 указателей, выделяла для каждого из них память и заносила бы музыкальные ноты до, ре, ми, фа, соль, ля, си в соответствующие области памяти.