
- •8. Интервальный тип данных. Структурированные типы данных. МаСсивы
- •8.1. Интервальный (индексный) тип данных
- •8.2. Структурированные типы данных. Массивы. Их свойства, описание, ввод и вывод
- •8.3. Инициализация массивов
- •8.4. Работа с одномерными (линейными) массивами
- •8.5. Работа с двухмерными массивами (матрицами)
- •8.6. Динамические массивы
- •8.6.1. Двумерные динамические массивы
- •8.7. Передача массивов в качестве параметров в подпрограммы. Открытые массивы
- •Параметры-массивы и параметры-строки
8.6. Динамические массивы
Необходимость работы с большими по числу элементов массивами явилась главной причиной для разработки методов работы с дополнительной динамической памятью. Одним из ее основных результатов явился тип данных "указатель", который в двух словах (по два байта каждое) содержит адрес одиночной величины или массива. Этот тип рассмотрен в Главе 3. Указатель на адрес величины a получается путем использования префикса ^ перед ней: ^a. Для засылки в указатель адреса уже существующей переменной применяют операцию получения адреса переменной (префикс @ перед ее именем или обращение к функции addr()), для обращения к значению переменной, адрес которой хранится в указателе, к нему примеряется операция разыменования, обозначаемая при помощи суффикса ^.
Выше были рассмотрены обычные массивы, у которых размер устанавливается при описании и не изменяется при выполнении программы, при этом фактически для работы может использоваться не все описанное количество элементов. Такие массивы называют статическими. Данный тип массивов всегда существовал в Паскале. Он удобен при относительно небольших числах элементов в массиве. Если переменная является статическим массивом, то она является хранилищем его данных и имеет размер, равный произведению количества элементов на их размер.
В отличие от статических, динамическими называют массивы, размер которых может изменяться во время исполнения программы. С одной стороны, это дает возможность работать с массивами, не ограничивая заранее их размер предельными объёмами. С другой стороны, более рационально используется память вычислительного устройства за счет выделения массивам реально необходимых ее объёмов. Динамические массивы появились в более поздних версиях Паскаля, с появлением Delphi 4. В отличие от статического случая, переменная динамического массива не содержит все элементы массива, а является указателем на область в памяти, где лежат данные элементы, т.е. на первый элемент массива. Использование динамических массивов освобождает от непосредственного использования указателей в простейших случаях работы с динамической памятью.
Для поддержки работы с динамическими массивами в языке программирования должны быть соответствующие операторы, встроенные функции, добавлены новые возможности прежним операторам.
Как и для обычных массивов, описание динамического массива можно задать при помощи предварительного описания типа (указывается имя динамического массива и тип элементов – один из базовых типов), можно без него - непосредственно при описании переменной. Оба вида описания динамического массива подобны описанию статического с той разницей, что не указывается размерность. Для многомерных массивов также допускается вложенность.
Пример 1. Описания динамических массивов:
type T1DByteArray : Array of Byte; {Предварительное описание одномерного динамического массива байтовых величин}
T1DIntArray = array of integer; {Предварительное описание одномерного динамического массива целых величин}
T2DStringArray : Array of Array of string; {Предварительное вложенное описание двумерного динамического массива строковых величин}
var B: T1DByteArray; {Присвоение переменной B типа одномерного динамического массива байтовых величин T1DByteArray }
StringMas: T2DStringArray; {Присвоение переменной StringMas типа двумерного динамического массива строковых величин T2DStringArray }
Ar_Int: array of integer; {Непосредственное описание переменной Ar_Int как одномерного динамического массива целых величин}
При объявлении динамического массива место под него не отводится. Переменная динамического массива представляет собой обычный указатель (4 байта) на начало массива. Если массив еще не объявлен либо количество элементов в нем равно 0 (массив пуст), то переменная равна nil.
Если динамический массив не пуст, то его действительный размер всегда на 8 байт больше того пространства памяти, которое занимают его элементы за счет двух дополнительных 4-байтовых величины. Сразу перед данными (по отрицательному смещению -4) в памяти помещается индикатор количества элементов в массиве. Перед ним (по смещению -8) находится счетчик ссылок на массив. Этот счетчик ссылок позволяет иметь несколько переменных, ссылающихся на одни и те же данные в массиве и не заботиться об управлении памятью. Компилятор самостоятельно следит за доступом к данным и при уменьшении счетчика ссылок до 0 освобождает всю память массива. Нумерация элементов в динамических массивах, в отличие от статических, начинается с 0 (как в языке С).
Присваивание динамических массивов вида Array_A:= Array_B по аналогии со статическими возможно, когда:
1) оба массива описаны как динамические и имеют одинаковый тип элементов,
2) размер массива Array_A не превышает (больше либо равен) размеру Array_B, либо Array_A=nil.
При выполнении присваивания динамических массивов они имеют общую память и изменения значений элементов в одном массиве приводят к таким же изменениям и во втором. Если затем над одним из массивов выполняются преобразования, меняющие его структуру, то такая общность значений элементов теряется и связь значений элементов массивов утрачивается. Присвоение динамического массива статическому компилятор запрещает, для этого необходимо использовать поэлементную запись из динамического в статический массив.
Удалить динамический массив можно несколькими способами: применением функции Finalize или установкой нулевой длины массива.
Помимо возможности описания массивов без указания их размерности, в Паскале введены следующие основные функции для работы с динамическими массивами:
1) SetLength (mas, number)- устанавливает новый размер массива mas равным number (число элементов);
2) Length (mas)- возвращает количество элементов в массиве mas;
3) Low (mas)_- возвращает индекс первого элемента в массиве mas (всегда 0 для динамических массивов);
4) High (mas)- возвращает индекс последнего элемента в массиве mas;
5) Copy(mas, start_imdex, number) - возвращает подмножество из number элементов массива mas, начиная с номера start_imdex;
6) Slice - используется при передаче динамического массива в процедуры в качестве открытого массива (open arrays).
Сразу после объявления динамический массив пуст. Для работы с ним необходимо задать число элементов в нем с использованием функции SetLength. При повторном применении SetLength к массиву его размер изменится. Если новое значение размера больше предыдущего, то а нем сохраняются все прежние значения и в конце добавляются новые нулевые элементы. Если новый размер меньше предыдущего, то массив обрезается до нового размера.
Пример 2. Задание длины массива Ar_Int из примера 1:
SetLength(Ar_Int,10);
В результате в массиве будет задано 10 элементов - от Ar_Int[0] до Ar_Int[9].
Обращение к первому элементу динамического массива Dyn_Ar имеет вид Dyn_Ar[0] или Dyn_Ar[Low (Dyn_Ar)].
Пример 3. Обнуление всех элементов динамического массива Dyn_Ar типа integer:
FillChar(Dyn_Ar [0],Length(Dyn_Ar)*sizeof(integer));
При работе с динамическими, как и статическими массивами необходимо следить, чтобы номера элементов в массивах не выходили за текущие их границы. Иначе компилятор прекращает выполнение программы с выдачей сообщения об ошибке.
Пример 4 с использованием динамических массивов А,В и статического С:
var A,B: array of integer;
C: array[0..10] of integer;
Len, i: Integer;
begin
Len:=4; SetLength (A,Len); //Создание дин. массива А(0,0,0,0)
for i:=0 to Len-1 do A[i]:=2*i; // Создание дин. массива А (0,2,4,6)
B:=A; // Копирование дин. массива А (0,2,4,6) в дин массив В
for i:=0 to Len-1 do C[i]:=A[i]; // Засылка значений в стат. массив С: C(0,2,4,6)
A[1]:=10; //Изменение значения первого элемента массива А: A[1]:=10
writeln (' Enter values of massiv elements:');
for i:=0 to Len-1 do
writeln (' A[',i,']=',A[i], ' B[',i,']=',B[i], ' C[',i,']=',C[i]);
A:=Copy(A,1,2); //Запись в 2 первых элемента (А[0],A[1]) массива А прежних значений А[1], A[2]
writeln (' Finish values of massiv elements:');
for i:=0 to Len-3 do
writeln (' A[',i,']=',A[i],' B[',i,']=',B[i], ' C[',i,']=',C[i]);
SetLength(A,0); SetLength(B,0); //Удаление динамических массивов А и В
end.
B итоге работы программы на экране пользователя выдается информация:
Первая выдача значений элементов массивов показывает, что присвоение B:=A в начале задает полную идентичность динамических массивов A и B - изменение A[1]:=10 автоматически вызвало такое же изменение в массиве В[1]:=10. Статический массив, в который поэлементно были засланы элементы A, не изменился.
Вторая выдача значений массивов показывает, что применение функции Copy к массиву A вызвало разрыв связи динамических массивов A и B.