Методические указания для студентов 1 курса факультета математики, механики и компьютерных наук
Вид материала | Методические указания |
Содержание1.4Динамическая память и динамические переменные 1.5Процедуры New и Delete |
- Программа курса «история и методология математики» для студентов дневного отделения, 151.46kb.
- Методические указания курса «культурология» Для студентов биологического факультета, 331.04kb.
- Войта Елена Александровна, магистрант факультета математики, механики и компьютерных, 129.35kb.
- Методические указания по изучению дисциплины Для студентов 4 курса заочного факультета, 3497.6kb.
- Методические указания и контрольные задания по английскому языку для студентов II курса, 375.13kb.
- И. И. Мечникова Институт математики, экономики и механики Кафедра математического обеспечения, 900.66kb.
- Методические указания к изучению курса «История мифологии» для студентов 1 курса факультета, 420.44kb.
- Методические указания к выполнению курсовой работы по дисциплине «Основы научных исследований», 403.99kb.
- Отчет по самообследованию дополнительной профессиональной программы для получения дополнительной, 317.2kb.
- Методические указания к выполнению курсовой работы по дисциплине «Оценка качества продовольственного, 856.1kb.
1.4Динамическая память и динамические переменные
Память, отводимая под данные программы, делится на статическую, автоматическую и динамическую. Статическая память выделяется до начала работы программы под глобальные переменные и константы и освобождается только при завершении программы. Автоматическая память выделяется на программном стеке под локальные переменные при вызове подпрограммы, а после завершения подпрограммы автоматически освобождается. При этом статическая память инициализируется нулевыми значениями, а автоматическая – не инициализируется (это делается для ускорения вызова подпрограммы).
Поскольку как программный стек, так и область статической памяти, выделяются заранее в момент начала работы программы, статическая и автоматическая память имеют фиксированный размер. Однако во многих задачах в разные моменты работы программы требуется существенно различное количество памяти. Отводить для этого фиксированный максимально необходимый размер памяти – расточительство. С данной проблемой мы уже сталкивались при работе с массивами: при описании массива указывается его максимально возможный размер, текущая же заполненность массива, как правило, меньше его размера.
Динамическая память, называемая также кучей, выделяется явно по запросу программы из ресурсов операционной системы и контролируется указателем. Она не инициализируется автоматически и должна быть явно освобождена. В отличие от статической и автоматической памяти динамическая память практически не ограничена (ограничена лишь размером оперативной памяти) и может динамически меняться в процессе работы программы. Недостатки динамической памяти являются продолжением ее достоинств. Во-первых, поскольку она контролируется указателем, доступ к ней осуществляется несколько дольше, чем для статической и автоматической памяти. Во-вторых, программист сам должен заботиться о выделении и освобождении памяти, что чревато большим количеством потенциальных ошибок.
1.5Процедуры New и Delete
Для выделения динамической памяти, контролируемой типизированным указателем, используется стандартная процедура New, для освобождения – стандартная процедура Dispose. Если pt – указатель на тип T, то вызов New(pt) распределяет в динамической памяти переменную типа T и записывает в pt адрес этой переменной:
Переменная, распределенная в динамической памяти, называется динамической переменной. Она не имеет своего имени и для доступа к ней используется разыменованный указатель pt. После работы с динамической переменной занимаемая ею память должна быть освобождена вызовом стандартной процедуры Dispose, например: Dispose(pt). Таким образом, динамическая переменная существует между вызовами New и Dispose:
var pt: real;
begin
New(pt);
pt:=2.8;
pt:=pt*2;
...
Dispose(pt);
end.
Выделение и освобождение динамической памяти выполняется специальной подсистемой программы, называемой менеджером кучи. Менеджер кучи хранит список всех незанятых блоков в динамической памяти. При вызове New менеджер кучи ищет незанятый блок подходящего размера, выделяет в нем память и модифицирует список незанятых блоков. При вызове Dispose блок вновь помечается как свободный. После завершения программы вся выделенная для нее динамическая память автоматически возвращается назад системе.
Если динамическая память выделяется в подпрограмме для решения локальных задач данной подпрограммы, то она должна быть освобождена в конце работы этой подпрограммы. Исключение составляют так называемые «создающие» подпрограммы, основным предназначением которых является вернуть объект, созданный в динамической памяти. Например:
function NewInteger(i: integer): pinteger;
begin
New(Result);
Result:=i;
end;
var pi: pinteger;
begin
pi:= NewInteger(5);
...
При своем вызове функция NewInteger возвращает указатель на динамическую переменную, которая должна быть впоследствии освобождена. Основная проблема состоит в том, что NewInteger не является стандартной функцией, и при ее вызове можно забыть, что она выделяет динамическую память. Один из способов «напомнить» об этом программисту – дать функции имя, свидетельствующее о ее «создающей» способности. Например, имя такой функции может начинаться с префикса New или Create.
Пример. Массив указателей на переменные разных типов.
В некоторых задачах возникает необходимость хранить в массиве данные различных типов. Пусть в массиве требуется хранить данные типа integer, real и shortstring.
Приведем вначале решение, не использубщее указатели.
Решение 1. Используем записи с вариантами. Опишем следующие типы:
type TVar=(tInt,tReal,tStr);
Variant = record
case t: TVar of
tInt: (i: integer);
tReal: (r: real);
tStr: (s: shortstring);
end;
Теперь опишем массив записей Variant и добавим в него несколько значений:
var A: array [1..10] of Variant;
begin
A[1].t:=tInt; A[1].i:=5;
A[2].t:=tReal; A[2].r:=3.14;
A[3].t:=tStr; A[3].s:='Delphi';
end.
Для вывода содержимого массива, очевидно, следует воспользоваться циклом
for i:=1 to 3 do
case A[i].t of
tInt: writeln(A[i].i);
tReal: writeln(A[i].r);
tStr: writeln(A[i].s);
end;
Такое решение имеет важный недостаток: каждый элемент массива имеет размер, определяемый самым большим типом shortstring, что расточительно.
Решение 2. В вариантной части записи Variant будем хранить не значения соответствующих типов, а указатели на них:
type
TVar=(tInt,tReal,tStr);
pinteger=integer;
preal=integer;
pshortstring=shortstring;
Variant = record
t: TVar;
case t: TVar of
tInt: (pi: pinteger);
tReal: (pr: preal);
tStr: (ps: pshortstring);
end;
Будем добавлять в такой массив указатели на переменные разных типов:
var A: array [1..10] of Variant;
begin
A[1].t:=tInt; New(A[1].pi); A[1].pi:=5;
A[2].t:=tReal; New(A[2].pr); A[2].pr:=3.14;
A[3].t:=tStr; New(A[3].ps); A[3].ps:='Delphi';
Для вывода содержимого такого массива воспользуемся следующим циклом:
for i:=1 to 3 do
case A[i].t of
tInt: writeln(pinteger(A[i].p));
tReal: writeln(preal(A[i].p));
tStr: writeln(pstring(A[i].p));
end;
В данном решении суммарный объем данных определяется не размером максимального типа данных, а реальным содержимым в момент выполнения программы. По окончании работы с массивом A динамическую память, занимаемую его элементами, следует освободить. Поскольку параметр процедуры Delete имеет тип pointer, то для освобождения занимаемой памяти можно передать любое из полей-указателей, например, pi:
for i:=1 to 3 do
Delete(A[i].pi);