Массив представляет собой структуру данных, позволяющую хранить под одним именем совокупность данных одного любого конкретного типа. Массив характеризуется именем, типом хранимых элементов, размером (числом элементов), нумерацией элементов и размерностью. Различают массивы статические, размеры которых устанавливаются в момент их объявления, и динамические, размеры которых могут изменяться во время выполнения. В функции или процедуры можно передавать в качестве параметров как статические, так и открытые массивы, размер которых неизвестен. Кроме того, можно передавать открытые массивы констант, содержащие значения различных типов. Для массивов определен ряд операций и функций.
Объявление статического массива имеет вид:
var <имя>: array [<ограниченный тип>,...] of <тип элементов>;
Например:
var A: array [1..10] of integer; //одномерный массив
A2: array[1..10,1..3] of integer; //двумерный массив
{массив с индексом перечислимого типа:}
Type color=(red,yellow,green);
var ACol: array [color] of integer;
Ch: array ['a'..'z'] of char; //массив символов
Доступ к элементам массива осуществляется по индексам. Например, A[i] или A2[4,3].
Объявление переменной или типизированной константы типа массива можно совмещать с заданием начальных значений, которые перечисляются после знака равенства, разделяются запятыми и заключаются в круглые скобки. Например:
Var A1 : array[0..10] of integer =
(1,2,3,4,5,6,7,8,9,10,11);
Const A1 : array[0..10] of integer =
(1,2,3,4,5,6,7,8,9,10,11);
Массивы символов эквивалентны строкам, и с ними можно во многом обращаться как со строкой. Для массивов символов можно использовать задание начальных значений и по элементам, и сразу всей строке. Например, эквивалентны друг другу следующие объявления
Const S:array[0..3] of char = ('A','B','C','D');
Const S:array[0..3] of char = 'ABCD';
При задании начальных условий для многомерных массивов список значений по каждой размерности заключается в скобки, например:
type Ar3 = array[1..4,1..3,1..2] of integer;
const A3: Ar3=(((0,1),(2,3),(4,5)),
((6,7),(8,9),(10,11)),
((12,13),(14,15),(16,17)),
((18,19),(20,21),(22,23)));
Отметим, что в конструкциях, подобным объявлению константы A3, легко запутаться в открывающих (закрывающих) скобках. В Delphi существует специальное сочетание клавиш Alt+[ (Alt+]), позволяющее осуществить поиск соответствующей открывающей (закрывающей) скобки.
Многомерный динамический массив определяется как динамический массив динамических массивов и т.д. Например, определим двумерный динамический массив:
var A2: array of array of integer;
Для выделения памяти под динамический массив необходимо использовать процедуру SetLength, передавая ей в качестве параметров несколько размеров. Например, для задания динамического массива размерностью 3 на 4 для объявленной переменной A2 необходим оператор
SetLength(A2,3,4);
Отметим, что можно создавать и непрямоугольные массивы, в которых, например, второй размер не постоянен и варьируется в зависимости от номера строки [1].
При создании функции или процедуры работы с массивами в ее объявление нельзя включать описание индексов. Например, объявление
procedure MyProc(A: array[1..10] of Integer);
будет расценено как синтаксическая ошибка и вызовет соответствующее сообщение компилятора. Правильное объявление:
type ta = array[1..10] of Integer;
procedure MyProc(A: ta);
Функции и процедуры в Object Pascal могут воспринимать в качестве параметров не только массивы фиксированного размера, но и так называемые открытые массивы, размер которых неизвестен. В этом случае в объявлении функции или процедуры они описываются как массивы базовых типов без указания их размерности. Например:
procedure SumArray(A:array of integer;
var B: array of integer);
При таком определении передаваемый в функцию первый массив будет копироваться и с этой копией – массивом A будет работать процедура. Второй открытый массив определен как var. Этот массив передается по ссылке, т.е. он не копируется, и процедура будет работать непосредственно с исходным массивом. Открытый массив воспринимается в теле процедуры или функции как массив с целыми индексами, начинающимися с нуля. Размер массива может быть определен функциями Length – число элементов и High – наибольшее значение индекса. Очевидно, что всегда High = Length - 1.
При вызове функции или процедуры с параметром в виде открытого массива можно использовать в качестве аргумента конструктор открытого массива, который формирует массив непосредственно в операторе вызова. Список элементов такого конструктора массива заключается в квадратные скобки, а значения элементов разделяются запятыми. Например, функцию Sum, объявленную в специальном математическом модуле Math как
function Sum(const Data: array of Double): Extended;
которая суммирует элементы числового массива, можно вызвать следующим образом:
Sum([1.2,4.45,0.1]);
Множество – это группа элементов, которая ассоциируется с идентификатором (именем) и с которой можно сравнивать другие величины, чтобы определить: принадлежат ли они этому множеству. Один и тот же элемент не может входить в множество более одного раза. Как частный случай, множество может быть пустым. Множество определяется перечислением элементов, заключенных в прямоугольные скобки. Такая форма определения называется конструктором множества.
Например, если множество возможных единичных символов, которые могут быть получены в ответ на вопрос программы "Yes/No", содержит символы y, Y, n и N, то это множество можно описать конструктором ['y','Y','n','N']. Для определения, принадлежит ли переменная множеству, служит операция in. Например, проверить, дал ли пользователь один из допустимых ответов, можно оператором:
if (key in ['y','Y','n','N'])
then <оператор, выполняемый при допустимом ответе>
Множества могут содержать не только отдельные значения, но и ограниченные типы. Например, если необходимо контролировать символы, вводимые пользователем при вводе целого положительного или отрицательного числа, можно определить множество ['0'..'9', '+', '-'] и использовать его, например, при обработке события OnKeyPress любого окна редактирования с помощью следующего кода:
if not(Key in ['0'..'9', '+', '-']) then Key:=#0;
Подобный оператор не позволяет пользователю ввести символы, отличные от имеющихся в множестве.
В приведенных операторах множество использовалось непосредственно, заранее не объявляясь в виде типа. Но если, например, в приложении в ряде мест надо проводить проверки, аналогичные приведенным выше, то целесообразнее объявить переменную или типизированную константу типа множества или тип множества и несколько переменных этого типа. Объявление типа множества делается в форме set of <базовый тип>. Приведем примеры.
var K: set of Char=['0'..'9', '+', '-']; // Объявление
глобальной переменной с инициализацией
// Объявление типизированной константы
const K1: set of Char=['0'..'9', '+', '-'];
// Объявление типа множества
type TDigit = set of '0'..'9';
var D1, D2: TDigit; // и переменных этого типа
Далее в программе допустимы следующие операторы.
...
if ((key in K)OR(key in K1)) then ...
...
D1:=['0','1'];
D2:=['2'..'9'];
Помимо операции in для множеств определен еще ряд операций: объединение, пересечение, операции отношения и ряд других (табл. 3.5).
Таблица 3.5.
Обозначение |
Операция |
Типы операндов |
Тип результата |
Пример |
+ |
объединение |
Set |
Set |
Set1+Set2 |
– |
разность |
Set |
Set |
S-T |
* |
пересечение |
Set |
Set |
S*T |
<= |
подмножество |
Set |
Boolean |
Q<=MySet |
>= |
включающее множество |
Set |
Boolean |
S1>=S2 |
= |
эквивалентность |
Set |
Boolean |
S2=MySet |
<> |
неэквивалентность |
Set |
Boolean |
MySet<>S1 |
in |
является элементом |
порядковый, Set |
Boolean |
A in Set1 |
В указанных операциях действуют следующие правила.
Z является элементом X+Y, если он является элементом X или Y, или и X, и Y.
Z является элементом X-Y, если он является элементом X, но не является элементом Y.
Z является элементом X*Y, если он является элементом и X, и Y.
Выражение X<=Y возвращает true, если каждый элемент X является элементом Y.
Выражение X>=Y эквивалентно выражению Y<=X.
Выражение X=Y возвращает true, если X и Y содержат точно одни и те же элементы. В противном случае true будет возвращено выражением X<>Y.
Различают файлы трех видов: текстовые файлы, типизированные файлы и нетипизированные файлы.
Текстовые файлы состоят из последовательностей символов, разбитых на строки. В Object Pascal предопределен тип TextFile, соответствующий текстовому файлу. Таким образом, объявление файловой переменной может иметь вид:
var <имя файловой переменной>: TextFile;
Работа с текстовыми файлами осуществляется процедурами и функциями файлового ввода/вывода. Основные процедуры чтения – Read, ReadLn, Write и WriteLn.
Типизированные файлы являются двоичными файлами, содержащими последовательность однотипных данных. Объявление файловых переменных таких файлов имеет вид:
var <имя файловой переменной>: file of <тип данных>;
var F: file of real; // пример
Процедуры чтения и записи Read и Write для типизированных файлов не отличаются от соответствующих процедур для текстовых файлов. Не определены процедуры Readln и Writeln, но есть процедура Seek, позволяющая перемещаться по файлу не только последовательно, как в текстовых файлах, а сразу переходить к требуемому элементу. Имеется также функция FilePos, возвращающая текущую позицию в файле.
Нетипизированные файлы – это двоичные файлы, которые могут содержать самые различные данные в виде последовательности байтов. Программист при чтении этих данных сам должен разбираться, какие байты к чему относятся. Тип файловой переменной нетипизированного файла объявляется следующим образом:
var <имя файловой переменной>: file;
Открытие нетипизированных файлов осуществляется процедурами Reset и Rewrite, синтаксис которых несколько отличен от аналогичных процедур для других видов файлов тем, что в этих процедурах указывается размер одной записи в байтах. Для записи и чтения данных в нетипизированных файлах имеются процедуры BlockRead и BlockWrite, которые читают или записывают указанное в них число записей.
Указатель является величиной, указывающей на некоторый адрес в памяти, где хранятся какие-то данные. Указатели бывают типизированные, указывающие на данные определенного типа, и нетипизированные (типа pointer), которые могут указывать на данные произвольного типа. Используются указатели обычно для работы с объектами в динамически распределяемой области памяти, особенно при работе с записями.
Объявление указателя имеет вид:
type <имя типа указателя> = ^<тип данных>;
F1 = ^real; // типизированный указатель
F2 = pointer; // нетипизированный указатель
Предопределена операция @, возвращающая адрес своего операнда: переменной, функции, процедуры, метода. Иначе говоря, операция @ создает указатель на свой операнд. При этом если X – переменная, то @X возвращает адрес X, причем при директиве компилятора {$T–} (включена по умолчанию) тип результата pointer. В случае функции или процедуры с именем F – @F возвращает точку входа, причем тип результата всегда pointer. Также существует предопределенная константа nil, которая обычно присваивается указателям, которые в данный момент ни на что не указывают.
Чтобы получить доступ к данным, на которые указывает типизированный указатель, надо применить операцию его разыменования. Она записывается с помощью символа ^, помещаемого после указателя. Например, для того, чтобы по адресу объявленного типизированного указателя на тип real F1 находилось какое-либо число, необходим оператор вида F1^:=0.18;. Операция разыменовывания не применима к типу pointer. Для разыменовывания подобного указателя сначала необходимо привести его к другому типу.
Процедурные типы данных также являются указателями на любые определенные в программе процедуры и функции. При их использовании необходимо следить за соответствием параметров, передаваемых переменной процедурного типа, формальному набору параметров функции или процедуры, на тип которой указывает процедурная переменная.