logo
Программирование в среде Delphy / Программирование в среде Delphi

16.1. Динамические переменные

Динамические переменные представляют собой указатели на области памяти, где хранятся какие–то данные. Каждая из таких переменных занимает в памяти 4 байта. Например, в разделе объявления переменных можно определить указатель на строку следующим образом:

Var PS:^String;

Знак «^» – тильда, поставленный перед типом String, обозначает описание указателя на память, которая может быть выделена строке. Переменная PS будет занимать в памяти 4 байта, в них и будет храниться адрес начала выделенной памяти для строки. Здесь под адресом будем понимать не физический адрес памяти, а особым образом рассчитанный адрес внутри участка памяти, выделенного для динамических переменных. Этот участок памяти описывается отдельным дискриптором, как это принято в защищенном режиме работы процессора. Весь процесс выделения и освобождения динамической памяти контролируется системой Delphi. Графически это можно представить так:

Память под динамические переменные выделяется и освобождается во время выполнения программы по мере необходимости. Приведем основные процедуры и функции для работы с динамическими переменными:

Procedure New(var P:Pointer); – процедура выделения памяти под динамическую переменную P. Здесь тип Pointer определяет любой указатель на область памяти. Эта процедура выделяет блок памяти такой длины, как это было указано в типе динамической переменной P. В P записывается адрес начала блока выделенной памяти;

Procedure Dispose(var P:Pointer); – процедура освобождения динамической памяти, которая ранее была выделена процедурой New для переменной P;

Procedure GetMem(var P:Pointer; Size:Integer); – процедура выделения блока динамической памяти длиной Size байт и записи адреса начала этой памяти в переменную P. Эта процедура позволяет выделять блок памяти любой длины, независимо от типа указателя P;

Procedure FreeMem(var P:Pointer; Size:Integer); – процедура освобождения динамической памяти, которая ранее была выделена переменной P процедурой GetMem;

Function AllocMem(Size:Cardinal):Pointer; – функция возвращает указатель на блок выделенной динамической памяти длиной Size байт и обнуляет эту память, в отличие от процедуры GetMem. Освобождать эту память можно процедурой FreeMem;

Var AllocMemCount:Integer; – системная переменная, которая определяет число динамических блоков памяти для данного приложения. Эта переменная доступна только для чтения;

Var AllocMemSize:Integer; – системная переменная, которая определяет общий размер всех динамических блоков памяти для данного приложения. Такие переменные позволяют контролировать процесс выделения и освобождения динамической памяти;

PS

4 байта Динамическая память

Адрес

Function SysGetMem(size:Integer):Pointer; – функция выделения системной динамической памяти. Эта память может быть доступна одновременно нескольким процессам;

Function SysFreeMem(var P:Pointer):Integer; – функция освобождения системной динамической памяти, которая ранее была выделена функцией SysGetMem;

Function Addr(X):Ponter; – функция получения адреса или указателя на любую переменную X. Эта функция эквивалентна оператору «@». Можно получить адрес переменной, записав:

P:=Addr(x); или

P:=@X;.

Процесс выделения и освобождения памяти довольно сложен и находится под контролем операционной системы. Память под динамические переменные выделяется в специальной области памяти Heap–области или так называемой «куче». Для предотвращения сильной фрагментации этой области памяти необходимо придерживаться следующего правила: освобождать память следует в обратном порядке по сравнению с ее выделением. Другими словами, если мы выделили память под переменную P1, а затем под переменную P2, то освобождать следует сначала память, выделенную под переменную P2, и только потом освобождать память, выделенную под переменную P1.

Для доступа к содержимому динамической памяти следует после имени указателя ставить символ «^» – тильда.

Рассмотрим пример программы умножения матрицы на вектор, но при этом матрицу и вектор расположим в динамической области памяти. Размер матрицы будем задавать в компоненте Edit1. Матрицу А будем выводить в компоненте StringGrid1, вектор X – в компоненте StringGrid2, а результат умножения поместим в компоненте StringGrid3. Тогда обработчик события нажатия клавиши «Вычислить» примет вид

Procedure Button1Click(Sender:Tobject);

Type Ta=array[1..1] of Extended;

// Тип одномерного массива вещественного типа

Tpa=^Ta; // Тип указателя на одномерный массив вещественного типа

Tapa=array[1..1] of Tpa; // Тип массива указателей

Tpapa=^Tapa; // Тип указателя на массив указателей

Var Px,Py:Tpa; // Указатели на массивы X и Y

Pa:Tpapa; // Указатель на массив указателей на одномерные массивы

// вещественных чисел.

i,j,n:Integer; // Внутренние переменные

Procedure MulAX(n:integer, var Px,Py:Tpa; var Pa:Tpapa);

// Процедура умножения матрицы А на вектор X, результат в векторе Y

Var i,j:Integer; s:extended; // Внутренние переменные

Begin

For i:=1 to n do Begin // Цикл по строкам

S:=0;

For j:=1 to n do // Цикл по столбцам

s:=s+Pa^[i]^[j]*Px^[j];

Py^[i]:=s; // Запоминаем результат

End;

End;

Begin // Начало раздела действий процедуры Button1Click

n:=StrToInt(Edit1.Text); // Определяем размерность массивов

with StringGrid1 do Begin // Подготовка визуальных компонентов

ColCount:=n+1;

RowCount:=n+1;

FixedCols:=1;

FixedRows:=1;

End;

with StringGrid2 do Begin

ColCount:=2;

RowCount:=n;

FixedCols:=1;

FixedRows:=0;

End;

with StringGrid3 do Begin

ColCount:=2;

RowCount:=n;

FixedCols:=1;

FixedRows:=0;

End;

GetMem(Px,n*Sizeof(extended)); // Выделение памяти для массива X

GetMem(Py,n*Sizeof(extended)); // Выделение памяти для массива Y

GetMem(Pa,n*Sizeof(Pointer));

// Выделение памяти для указателей на строки массива A

For i:=1 to n do Begin

GetMem(Pa^[i],n*Sizeof(Extended));

// Выделение памяти под строки массива A

Px^[i]:=Random; // Задание случайным образом вектора X

StringGrid2.Sells[0,i-1]:=Floattostr(i); // Оформление таблиц

StringGrid2.Sells[1,i]:=Floattostr(Px^[i]);

StringGrid1.Sells[0,i-1]:=inttostr(i);

StringGrid1.Sells[i-1,0]:=inttostr(i);

StringGrid3.Sells[i-1,0]:=inttostr(i);

For j:=1 to n do Begin

Pa^[i]^[j]:=Random; // Задание случайным образом значений массива A

StringGrid1.Sells[j,i]:=Floattostr(Pa^[i]^[j]);

End;

end;

MulAX(n,Px,Py,Pa); // Вызов процедуры умножения матрицы на вектор

For i:=1 to n do Begin

StringGrid3.Sells[1,i-1]:=Floattostr(Py^[i]); // Отображение результатов

FreeMem(Pa^[n-i+1],n*sizeof(Extended);

// Освобождение в обратном порядке памяти для строк матрицы A

End;

FreeMem(Pa,n*sizeof(Pointer));

// Освобождение массива указателей на строки массива A

FreeMem(Py,n*sizeof(extended)); // Освобождение памяти для вектора Y

FreeMem(Px,n*sizeof(Extended)); // Освобождение памяти для вектора X

End;

В данном примере размерность массивов в принципе может быть любая. Динамической памяти под массивы выделяется ровно столько, сколько нужно для заданной размерности n.