7.1. Массивы (Array) и работа с ними
Массив — это регулярная структура данных, объявляемая специальной конструкцией языка
Array [ диапазоны индексов ] of ТипКомпонентов
Наиболее часто массив используют для хранения значений векторов, например:
VAR
V : Array [ 1..3 ] of Real;
объявляя тем самым структуру из трех значений типа Real, проиндексированных заданным диапазоном целых чисел (V[1], V[2] и V[3]). Если индексация компонентов (элементов) массива задается числовым диапазоном, как в приведенном примере, то надо соблюдать лишь два требования: во-первых, диапазон не должен принадлежать типу LongInt, т.е. он обязан «уместиться» максимум в типе Word, a во-вторых, произведение количества компонентов массива, задаваемого диапазоном индексов, на размер компонента в байтах, не может превышать 65520 байт (почти 64K). Последнее требование является общим не только для всех массивов, но и для прочих структур данных. Таким образом, могут быть описаны массивы
- 133 -
Array [9..99] of Char; { массив из 91 элемента }
Array [-10.. 10] of LongInt; { массив из 21 элемента }
Это очень удобно, так как позволяет не заботиться о приведении индексов к диапазону 1..N, как, например, приходится поступать при работе с Фортраном или некоторыми версиями Бейсика.
В общем случае ничто не обязывает объявлять диапазон индексов массива числами. В качестве индексов можно использовать любые перечислимые типы, как встроенные, так и вводимые. Индексы могут задаваться по-прежнему диапазоном, а если диапазон соответствует всему типу, то можно вместо него просто записать имя этого перечислимого типа:
TYPE
Monthtype = ( January, February, March, April, May );
ComplectType = Array [ MonthType ] of Word;
SpringType = Array [ March..May ] of Word;
VAR
Complect : ComplectType; { пять элементов типа Word }
Spring : SpringType; { три элемента типа Word }
Alpha : Array [ 'A'..'z'] of Char;
Switch : Array [Boolean] of Byte; { два элемента }
Элементы массивов будут индексироваться значениями заданных перечислимых типов или их диапазонов: Complect [January] — первый, a Spring[May] — последний элементы в своих массивах; аналогичен смысл обращений Alpha [ 'А' ] и Switch [True].
Рассмотренные массивы — одномерные, т.е. такие, у которых компоненты — скаляры. Разрешено объявлять массивы массивов:
TYPE
VectorType = Array [ 1..3 ] of Real; { вектор }
MatrixType = Array [ 1..10] of VectorType; { матрица10x3 }
Описание типа двумерного массива MatrixType могло быть записано по-другому:
TYPE
MatrixType = Array [ 1..10] of Array [ 1..3 ] of Real;
или как
MatrixType = Array [ 1..10, 1..3 ] of Real;
Последний вариант наиболее наглядно представляет описание матрицы. Количество измерений формально не ограничено, но сумма размеров всех компонентов массива не должна превосходить 64K.
- 134 -
Каждое измерение совершенно не зависит от остальных, и можно объявлять массивы с разными индексами:
VAR
M : Array [ -10..0, 'A'..'C', Boolean ] of Byte;
Эквивалентная запись:
M : Array [-10..0] of
Array [ 'A'..'C' ] of
Array [Boolean] of Byte;
Интересно, что тип элемента массива M зависит от числа указанных при нем индексов. Так,
M[0] — массив-матрица типа Array['A'..'C',Boolean] of Byte,
М[0, 'B'] — вектор типа Array[Boolean] of Byte,
M[0, 'B', False] — значение типа Byte.
Если будут использоваться различные уровни «детализации» многомерных массивов, то надо будет позаботиться о совместимости по типу. Так, при приведенном выше описании массива M нельзя реально поставить подмассив M[0] в оператор присваивания, так как это не по правилам совместимости. Надо переписать объявление типа примерно так:
TYPE
ArrayBType = Array[Boolean] of Byte;
ArrayCType = Array ['A'..'C' ] of ArrayBType;
ArrayMType = Array [-10..0] of ArrayCType;
VAR
B : ArrayBType;
C : ArrayCType;
M : ArrayMType;
и лишь после этого будут разрешены присваивания вида
M[ -1 ] := C;
B := M[ -1, 'A' ];
Подобные вопросы совместимости можно обойти, используя приведение типов, а также специальную процедуру Move Турбо Паскаля, но это будет не повышением эффективности программы, а скорее свидетельством непродуманного объявления типов в ней.
Турбо Паскаль позволяет записывать индексы не через запятую, а как бы изолировано:
M[ -3, 'B', True ] эквивалентно M[ -3 ][ 'B' ][ True ]
- 135 -
Компонентом массива может быть не только другой массив, но и запись, и указатель, и какой-либо другой тип. Если R — массив записей (RECORD), то доступ к полю каждой записи производится после указания индекса:
R[ i ].ПолеЗаписи
В памяти ПЭВМ массивы хранятся как сплошные последовательности компонентов, причем быстрее всего изменяется самый «дальний» индекс, если их несколько. В примере задания стартового значения многомерному массиву (см. разд. 5.2.2) порядок перечисления элементов (без учета скобок) соответствует порядку размещения значений в памяти. Адрес начала массива в памяти соответствует адресу его первого элемента (элемента с минимальными значениями индексов).
Турбо Паскаль имеет специальный режим компиляции, задаваемый ключом $R. Если вся программа или фрагмент ее компилировался в режиме {$R+}, то при обращении к элементам массивов будет проверяться принадлежность значения индекса объявленному диапазону, и в случае нарушения границ диапазона программа прервется с выдачей ошибки 201 (Range check Error). Напротив, в режиме {$R-} никаких проверок не производится, и некорректное значение индекса извлечет «как ни в чем не бывало» какое-нибудь значение — но, увы, не принадлежащее данному массиву. Обычно программу отлаживают в режимах $R+, а эксплуатируют при режиме $R-. Это несколько уменьшает размер ЕХЕ-файла и время его выполнения.
К двум совместимым массивам A и B применима только операция присваивания:
A := B;
которая копирует поэлементно массив B в массив A.
Всевозможные математические действия над массивами (матрицами) необходимо реализовывать самим или использовать специальные библиотеки (например, Turbo Numeric Toolbox).
Для совместимости с другими версиями Паскаля Турбо Паскаль допускает использование составных символов (. и .) вместо квадратных скобок:
M[ 0 ] эквивалентно M(. 0 .)
Кроме того, ключевое слово Array в описаниях массивов может предваряться зарезервированным словом PACKED (упакованный, сжатый):
- 136 -
VAR
X : PACKED Array [ 1..100 ] of Real;
В Турбо Паскале данные и так хранятся максимально плотно, и слово PACKED практически игнорируется. Мы рекомендуем избегать его включения в тексты программ.
В завершение отметим одну особенность компилятора Турбо Паскаля. Для многих языков программирования справедливо правило: работа с элементом массива занимает больше времени, чем со скалярной переменной (надо вычислять местоположение элемента в памяти). Если индексы при обращении к элементу задаются переменными или выражениями, то это верно и для Турбо Паскаля. Но если индекс элемента задается константой, то скорость обращения к нему будет максимальной, потому что компилятор в этом случае вычислит расположение элемента еще на этапе компиляции программы.