Навигация
Оглавление
Динамические массивы

Одним из мощнейших средств языка Delphi являются динамические массивы. Их основное отличие от обычных массивов заключается в том, что они хранятся в динамической памяти. Этим и обусловлено их название. Чтобы понять, зачем они нужны, рассмотрим пример:

var
N: Integer;
A: array[1..100] of Integer; // обычный массив
begin
Write('Введите количество элементов: ');
ReadLn(N);
...
end.



Задать размер массива A в зависимости от введенного пользователем значения невозможно, поскольку в качестве границ массива необходимо указать константные значения. А введенное пользователем значение никак не может претендовать на роль константы. Иными словами, следующее объявление будет ошибочным:

var
N: Integer;
A: array[1..N] of Integer; // Ошибка!
begin
Write('Введите количество элементов: ');
ReadLn(N);
...
end.



На этапе написания программы невозможно предугадать, какие именно объемы данных захочет обрабатывать пользователь. Тем не менее, Вам придется ответить на два важных вопроса:

На какое количество элементов объявить массив?


Что делать, если пользователю все-таки понадобится большее количество элементов?
Вы можете поступить следующим образом. В качестве верхней границы массива установить максимально возможное (с вашей точки зрения) количество элементов, а реально использовать только часть массива. Если пользователю потребуется большее количество элементов, чем зарезервировано Вами, то ему можно попросту вежливо отказать. Например:

const
MaxNumberOfElements = 100;
var
N: Integer;
A: array[1.. MaxNumberOfElements] of Integer;
begin
Write('Введите количество элементов (не более ', MaxNumberOfElements, '): ');
ReadLn(N);
if N > MaxNumberOfElements then
begin
Write('Извините, программа не может работать ');
Writeln('с количеством элементов больше , ' MaxNumberOfElements, '.');
end
else
begin
... // Инициализируем массив необходимыми значениями и обрабатываем его
end;
end.



Такое решение проблемы является неоптимальным. Если пользователю необходимо всего 10 элементов, программа работает без проблем, но всегда использует объем памяти, необходимый для хранения 100 элементов. Память, отведенная под остальные 90 элементов, не будет использоваться ни Вашей программой, ни другими программами (по принципу "сам не гам и другому не дам"). А теперь представьте, что все программы поступают таким же образом. Эффективность использования оперативной памяти резко снижается.

Динамические массивы позволяют решить рассмотренную проблему наилучшим образом. Размер динамического массива можно изменять во время работы программы.

Динамический массив объявляется без указания границ:

var
DynArray: array of Integer;

Переменная DynArray представляет собой ссылку на размещаемые в динамической памяти элементы массива. Изначально память под массив не резервируется, количество элементов в массиве равно нулю, а значение переменной DynArray равно nil.

Работа с динамическими массивами напоминает работу с длинными строками. В частности, создание динамического массива (выделение памяти для его элементов) осуществляется той же процедурой, которой устанавливается длина строк - SetLength.

SetLength(DynArray, 50); // Выделить память для 50 элементов

Изменение размера динамического массива производится этой же процедурой:
SetLength(DynArray, 100); // Теперь размер массива 100 элементов

При изменении размера массива значения всех его элементов сохраняются. При этом последовательность действий такова: выделяется новый блок памяти, значения элементов из старого блока копируются в новый, старый блок памяти освобождается.

При уменьшении размера динамического массива лишние элементы теряютяся.

При увеличении размера динамического массива добавленные элементы не инициализируются никаким значением и в общем случае их значения случайны. Однако если динамический массив состоит из элементов, тип которых предполагает автоматическую инициализацию пустым значением (string, Variant, динамический массив, др.), то добавленная память инициализируется нулями.

Определение количества элементов производится с помощью функции Length:

N := Length(DynArray); // N получит значение 100

Элементы динамического массива всегда индексируются от нуля. Доступ к ним ничем не отличается от доступа к элементам обычных статических массивов:

DynArray[0] := 5; // Присвоить начальному элементу значение 5
DynArray[High(DynArray)] := 10; // присвоить конечному элементу значение 10

К динамическим массивам, как и к обычным массивам, применимы функции Low и High, возвращающие минимальный и максимальный индексы массива соответственно. Для динамических массивов функция Low всегда возвращает 0.

Освобождение памяти, выделенной для элементов динамического массива, осуществляется установкой длины в значение 0 или присваиванием переменной-массиву значения nil (оба варианта эквивалентны):

SetLength(DynArray, 0); // Эквивалентно: DynArray := nil;

Однако Вам вовсе необязательно по окончании использования динамического массива освобождать выделенную память, поскольку она освобождается автоматически при выходе из области действия переменной-массива (удобно, не правда ли!). Данная возможность обеспечивается уже известным Вам механизмом подсчета количества ссылок.

Также, как и при работе со строками, при присваивании одного динамического массива другому, копия уже существующего массива не создается.

var
A, B: array of Integer;
begin
SetLength(A, 100); // Выделить память для 100 элементов
A[0] := 5;
B := A; // A и B указывают на одну и ту же область памяти!
B[1] := 7; // Теперь A[1] тоже равно 7!
B[0] := 3; // Теперь A[0] равно 3, а не 5!
end.



В приведенном примере, в переменную B заносится адрес динамической области памяти, в которой хранятся элементы массива A (другими словами, ссылочной переменной B присваивается значение ссылочной переменной A).

Как и в случае со строками, память освобождается, когда количество ссылок становится равным нулю.

var
A, B: array of Integer;
begin
SetLength(A, 100); // Выделить память для 100 элементов
A[0] := 10;
B := A; // B указывает на те же элементы, что и A
A := nil; // Память еще не освобождается, поскольку на нее указывает B
B[1] := 5; // Продолжаем работать с B, B[0] = 10, а B[1] = 5
B := nil; // Теперь ссылок на блок памяти нет. Память освобождается
end;



Для работы с динамическими массивами вы можете использовать знакомую по строкам функцию Copy. Она возвращает часть массива в виде нового динамического массива.

Не смотря на сильное сходство динамических массивов со строками, у них имеется одно существенное отличие: отсутствие механизма копирования при записи (copy-on-write).

 

Сайт создан в системе uCoz