Динамические переменные
Переменные, которые описываются в разделе varкакого-либо блока про- граммы или подпрограммы, называются статическими. Транслятор после анализа этого раздела, т.е. до выполнения программы, отводит каждой переменной в зависимости от ее типа соответствующее число ячеек памяти определенной длины и закрепляет их за данной переменной на все время работы блока. Они не могут использоваться под другие нужды, даже если в процессе работы про- граммы эта переменная становится ненужной. Статические переменные размещаются в непрерывной области памяти, имеющей небольшой объем, большая часть оперативной памяти при этом остается неиспользованной.
Динамическое распределение памяти широко используется для экономии компьютерных ресурсов при обработке данных большого объема, которые могут не уместиться в памяти, отводимой для статических переменных. При этом те переменные или объекты, которые становятся ненужными, уничтожаются, а освобожденное место используется для новых переменных или объектов. Это особенно эффективно в задачах, в которых число необходимых объектов зависит от обрабатываемых данных или от действий пользователя в процессе выполнения программы, т.е. заранее неизвестно. Для динамического распределения выделяется специальная область оперативной памяти – heap(куча). Динамическая память (куча) – это свободная часть оперативной памяти, которая доступна для использования в процессе работы программы. Как правило, она значительно больше памяти, отводимой для статических переменных. Динамическое размещение данных происходит непосредственно при выполнении про- граммы. При динамическом размещении заранее неизвестны ни количество, ни тип данных. К ним нельзя обращаться по именам, как к статическим переменным. Для работы с объектами в динамически распределяемой области памяти используются указатели. При этом сами указатели должны описываться в разделе var, т.е. являются статическими переменными.
Динамической переменнойназывается переменная, память для которой выделяется во время работы программы.
Динамическое распределение памяти в куче может производиться двумя способами: 1) с помощью процедур newи dispose
2) с помощью процедур getmemи freemem.
При первом способе выделение памяти для динамической переменной производится процедурой: procedurenew (имя указателя);
где имя указателя – имя переменной, являющейся типизированным указателем. Этой переменной передается адрес начала выделенной области памяти. Размер выделяемой области определяется размером памяти, необходимым для размещения того типа данных, который указан при объявлении указателя.
Например:
varp:^real;
… new(p);
Здесь описывается переменная p, являющаяся указателем на действительное значение. Процедура new выделяет память для переменной типа real, и переменная - указатель p будет содержать адрес первого байта памяти, выделенной для этой переменной. После того, как указатель приобрел некоторое значение, отличное от nil , то есть стал указывать на конкретный байт памяти, в выделенный участок памяти может быть помещено значение соответствующего типа. Чтобы получить доступ к данным, на которые указывает типизированный указатель, надо записать:
имя переменной -указателя ^
Символ ^ после имени указателя означает, что речь идет не о значении переменной-указателя, а о значении того динамического объекта, на который указывает эта переменная. Имя ссылочной переменной с последующим символом ^ называют динамической переменной (переменной с указателем).
Например:
vari: ^integer; a: ^real;
Begin
new(i); new(a); i^:=10; a^:=5.3/i^;
End.
Здесь a^ - число 0,53
a – адрес числа 0,53
Нельзя смешивать указатели и данные:
a:=sqr(a^)-i^; {неверно}
a^:=sqr(a); {неверно}
После того, как динамическая переменная стала ненужной, ее можно уничтожить, а освободившиеся байты вернуть в кучу. Освобождение памяти, динамически выделенной процедурой new, осуществляется процедурой dispose:
proceduredispose (имя указателя );
Применение процедуры dispose освобождает память, но не изменяет значение указателя, не делает его равным nil, хотя теперь указатель не указывает ни на что конкретное. Например, если р – указатель на динамическую переменную, память для которой выдела процедурой new(p), то процедура dispose(р) освобождает занимаемую динамической переменной память (байты, содержавшие p^, будут считаться свободными). При этом значение указателя р не изменяется, однако его использование приведет к порче значений других динамических переменных. Поэтому рекомендуется освободившемуся указателю присваивать значение nil: dispose(р); р:= nil;
Пример:
varp1, p2, p3:^integer;{указатели на переменные типа integer}
Begin
//выделяется память для динамических переменных типа integer new(p1); new(p2); new(p3);
//динамические переменные p1^, p2^ и p3^ получают значения p1^:=5; p2^:=3; p3^:=p1^+p2^;
writeln(‘сумма чисел равна ‘,p3^);
//освобождается память, занимаемая динамическими переменными dispose(p1); dispose(p2); dispose(p3);
End.
Второй способ динамического выделения памяти связан с применением процедуры Getmem для выделения памяти и Freemem для ее освобождения:
procedureGetmem(имя указателя, объем памяти в байтах);
procedureFreemem(имя указателя, объем памяти в байтах);
В этих процедурах могут использоваться не только типизированные, но и нетипизированные указатели.
Например: vara:pointer;
beginGetmem(a,4); … Freemem(a,4); end.
Если используется типизированный указатель, то объем необходимой памяти лучше всего определить функцией sizeof, так как размеры памяти, отводимой под тот или иной тип данных, могут изменяться в различных версиях компилятора. Функция sizeof(х) вычисляет длину в байтах для х.
Getmem(p,sizeof(real)); Freemem(p,sizeof(real));
Чтобы получить доступ к данным, на которые указывает нетипизированный указатель pointer, нельзя писать: имя переменной-указателя ^. Надо сначала привести его к другому типу указателя. Явное приведение типа переменной можно проводить для любых типов, имеющих одинаковый размер, с помощью конструкции: идентификатор типа (выражение).
Например:
typePint=^integer;
varp1,p2:Pint; p:pointer; i:integer;
выражение Pint(p) приведет тип указателя p к объявленному выше типу Pint, после чего его можно записать:
Pint(p):=p1; i:=Pint(p)^+p2^;
Пример:
typepint = ^integer;
varp1,p2:pint;p:pointer;
Begin
new(p1); new(p2); getmem(p,sizeof(integer));
p1^:=5; p2^:=10; {можно записать p:=p1;p^ уже нельзя} pint(p)^:=p1^;
writeln(pint(p)^+p2^); {результат 15} dispose(p1); dispose(p2); freemem(p,sizeof(integer));
End.
Динамическая память используется для организации динамических структур данных. Простейшими динамическими структурами являются стеки, очереди и списки. Элементы этих структур программируются в виде величин типа запись, причем одно из полей является указателем на следующий (предыдущий) элемент структуры. Это позволяет размещать данные в памяти независимо друг от друга, так что удаление элемента из структуры или добавление нового элемента не приводит к необходимости перемещать в памяти другие элементы, как это происходит, например, при использовании массивов.
Дата добавления: 2016-07-05; просмотров: 2099;