7.2.1. Массивы
Массивы в Object Pascal во многом схожи с аналогичными типами данных в других языках программирования. Отличительная особенность массивов заключается в том, что все их компоненты суть данные одного типа (возможно, структурированного). Эти компоненты можно легко упорядочить и обеспечить доступ к любому из них простым указанием его порядкового номера, например:
type
digit = array [0..9] of Char;
matrix = array [byte] of Single;
var
m : matrix;
d : digit;
i : integer;
begin
m[17] := ord(d[i-l])/10;
end.
Описание типа массива задается следующим образом:
<имя типа> = array [ <сп.инд.типов> ] of <тип>;
Здесь <имя типа> - правильный идентификатор; array, of - зарезервированные слова {массив, из); <сп.инд.типов> - список из одного или нескольких индексных типов, разделенных запятыми; квадратные скобки, обрамляющие список, - требование синтаксиса;
<тип> - любой тип Object Pascal.
В качестве индексных типов в Object Pascal можно использовать любые порядковые типы, имеющие мощность не более 2 Гбайт (т. е. Кроме LongWord И Int64)
Определить переменную как массив можно и непосредственно при описании этой переменной, без предварительного описания типа массива, например:
var
a,b : array [1..10] of Real;
Обычно в качестве индексного типа используется тип-диапазон, в котором задаются границы изменения индексов. Так как тип <тип>, идущий в описании массива за словом of, - любой тип Object Pascal, то он может быть, в частности, и другим массивом, например:
type
mat = array [0..5] of array [-2..2] of array [Char] of Byte;
Такую запись можно заменить более компактной:
type
mat = array [0..5,-2..2,char] of Byte;
Глубина вложенности структурированных типов вообще, а следовательно, и массивов - произвольная, поэтому количество элементов в списке индексных типов (размерность массива) не ограничено, однако суммарная длина внутреннего представления любого массива не может быть больше 2 Гбайт. В памяти ПК элементы массива следуют друг за другом так, что при переходе от младших адресов к старшим наиболее быстро меняется самый правый индекс массива.
Если, например,
var
a: array [1..2,1..2] of Byte;
begin
а[1,1] := 1;
а[2,1] := 2 ;
а[1,2] := 3;
а[2,2] := 4;
end.
то в памяти последовательно друг за другом будут расположены байты со значениями 1,3,2,4 . Это обстоятельство может оказаться важным при использовании стандартной процедуры копирования памяти MoveMemory.
В Object Pascal можно одним оператором присваивания передать все элементы одного массива другому массиву того же типа, например:
var
a,b : array [1..5] of Single;
begin
а := b;
end.
После этого присваивания все пять элементов массива а получат те же значения, что и в массиве в. Замечу, что объявление
var
a: array [1..5] of Single;
b: array [1..5] of Single;
создаст разные типы массивов, поэтому оператор
а := b;
вызовет сообщение об ошибке.
Над массивами не определены операции отношения. Нельзя, например, записать
if а = b then ...
Сравнить два массива можно поэлементно, например:
var
a,b : array [1..5] of Single;
eq : Boolean;
i : Byte;
begin
eq := True; for i := 1 to 5 do
if a[i] <> b[i] then
eq := False/if eq then
end.
Динамические массивы
В версии Delphi 4 впервые введены так называемые динамические массивы. При объявлении таких массивов в программе не следует указывать границы индексов:
var
A: array of Integer;
В: array of array of Char;
C: array of array of array of Real;
В этом примере динамический массив а имеет одно измерение, массив в - два и массив с - три измерения. Распределение памяти и указание границ индексов по каждому измерению динамических массивов осуществляется в ходе выполнения программы путем инициации массива с помощью функции setLength. В ходе выполнения такого оператора:
SetLength(А,3);
одномерный динамический массив а будет инициирован, т. е. получит память, достаточную для размещения трех целочисленных значений. Нижняя граница индексов по любому измерению динамического массива всегда равна 0, поэтому верхней границей индексов для а станет 2.
Фактически идентификатор динамического массива ссылается на указатель (см. гл. 9), содержащий адрес первого байта памяти, выделенной для размещения массива. Поэтому для освобождения этой памяти достаточно присвоить идентификатору значение nil (другим способом является использование процедуры Finalize):
var
А,В: array of Integer;
begin
// Распределяем память:
SetLength(A,10) ;
SetLength(B,20) ;
// Используем массивы:
// Освобождаем память:
А := NIL;
Finalize(В);
end;
При изменении длины уже инициированного динамического массива по какому-либо его измерению сначала резервируется нужная для размещения нового массива память, затем элементы старого массива переносятся в новый, после чего освобождается память, выделенная прежнему массиву. Чтобы сократить дополнительные затраты времени, связанные с изменением границ большого динамического массива, следует сразу создать массив максимальной длины.
В многомерных массивах сначала устанавливается длина его первого измерения, затем второго, третьего и т. д. Например:
var
A: array of array of Integer; //Двумерный динамический массив begin
//Устанавливаем длину первого измерения (количество столбцов):
SetLength(A,3) ;
//Задаем длину каждого столбца:
SetLength(A[0],3) ;
SetLength(A[l],3) ;
SetLength(A[2] ,3) ;
end;
Обратите внимание: в отличие от обычных массивов стандартного Паскаля (и Object Pascal), динамические массивы могут иметь разную длину по второму и следующим измерениям. В предыдущем примере определен квадратный массив 3х3. Однако ничто не мешает нам создать, например, треугольный массив:
SetLength(A,3) ;
//Задаем длину каждого столбца:
SetLength(A[0],3) ;
SetLength(A[l],4) ;
SetLength(A[2],5) ;
В многомерных динамических массивах каждый элемент любого из N-1 измерений (N - количество измерений) представляет собой динамический массив и, следовательно, нуждается в инициации. Вот как, например, можно инициировать вещественный кубический массив 3х3х3:
var
A: array of array of array of Real;
i, j: Integer;
begin
SetLength(A,3) ;
for i := 0 to 2 do
begin
SetLength(A[i],3) ;
for j := 0 to 2 do SetLength{A[i,j],3) ;
end;
end;