6. Описание типов
Ранее уже
приводились примеры описания переменных, в
которых их тип указывался в Var-секции явно или
на основе ранее объявленного пользовательского
типа .
Описание секции типов
начинается словом Type.
В табл. 4 дан пример
двух идентичных способов описания переменных
t, u, n.
Таблица 4
Явный
способ описания
переменных |
Описание
переменных с предварительным описанием их типа |
Var
t,u,n:(Mon, Tue,Wed, Thu,Fri,Sat,Sun); |
Type
DaysOfWeek = (Mon, Tue,Wed, Thu,Fri,Sat,Sun);
Var t,u,n: DaysOfWeek; |
В тех
случаях, когда явный и типизованный
способы описания переменных конкурируют,
следует всегда отдавать предпочтение способу
описания переменных с предварительным
объявлением их типа в секции Type. Такой способ позволяет:
а) конкретизировать
тип ;
б) четко выделить
множество переменных этого типа ;
в) повысить уровень
структурированности программы ;
г) снизить вероятность
путаницы в типах, когда переменные фактически
того же типа объявлены разными способами ;
д) уменьшить объем
текста за счет возможности быстрой ссылки на
ранее определенный тип, особенно в тех ситуациях,
когда этот тип используется для порождения новых
типов, переменных, функций и пр. в других секциях
или модулях.
В этой связи важно подчеркнуть, что
даже при совпадении базовых типов различие в
пользовательских типах может привести к
непредсказуемому поведению программы. Например,
в нижеследующей секции Type два
производных типа t1 и t2 имеют одинаковый базовый тип byte. Однако объявленные ниже в Var-секции переменные p1 и p2 будут
расценены системой как переменные разных типов.
Это обстоятельство может послужить причиной
недоразумений в ходе составления и/или
выполнения программы.
Type
t1 = byte;
t2 = byte;
Var
p1: t1;
p2: t2;
Корректным можно
считать следующий аналог:
Type
t1 = byte;
Var
p1,p2: t1;
7. Структурные типы
Структурные
типы представляют собой совокупность значений одного или нескольких различных типов.
Их наличие позволяет программисту
конструировать производные типы практически
любой сложности, что резко расширяет возможности
языка.
К числу структурных относятся
следующие типы:
[Set],
регулярные типы (массивы) [Array],
комбинированные типы (записи) [Record],
файловые типы [File],
классы [Class],
классовые ссылки [Class reference],
интерфейсы [Interface].
Ниже будут
подробно описаны первых четыре структурных типа
– регулярный, комбинированный, множественный и
файловый .
Три последних типа будут описаны
отдельно в разделах, посвященных
объектно-ориентированному программированию.
7.1.
Регулярные типы (массивы)
Массив – это структура
языка Object Pascal, представляющая собой упорядоченную
совокупность элементов одного типа .
Следует различать два вида массивов: массив-тип
и массив-переменную .
Массив-тип. Синтаксис ма cсива-типа:
<имя массива>
= Array [<тип индекса>, <тип
индекса>, …, <тип индекса>]
Of <тип элемента>;
Всякий массив
имеет размерность. Размерность определяется количеством
типов индексов, которые заключены в квадратные
скобки [ .. ].
Массив-тип предназначен для описания:
- структуры массива как типа
;
размерности массива;
типов индексов массива;
типа каждого элемента массива.
Так, в следующем
примере
Type
tA1: array [1 .. 10] of Real;
описана структура одномерного массива вещественных
элементов (Real), в котором индекс может
изменяться в диапазоне целых значений от 1 до 10.
Его элементами являются вещественные типы
tA1[1], tA1[2], tA1[3], …, tA1[9], tA1[10].
Другой пример:
Type
Color: (Red, Green); { перечислимый тип }
Z: array [1 .. 3, Color ] of Boolean; { массив }
В нем сначала
описан простой перечислимый тип Color. Ниже на его
основе описан двумерный массив Z логических (Boolean)
элементов. Первый индекс массива имеет целый тип,
а второй – тип Color. Таким образом, массив состоит из шести элементов – логических типов:
Z [1, Red], Z [1, Green],
Z[2, Red], Z[2, Green], Z[3, Red], Z[3, Green].
Массив-переменная.
Синтаксис маcсива-переменной:
<имя массива
> : Array [<тип индекса>,<тип
индекса>, …, <тип индекса>]
Of <тип элемента>;
Массив-переменная
отличается от массива-типа тем, что все его
элементы – это отдельные независимые
переменные, которые могут содержать различные
значения одного типа.
Массив-переменная может быть описан явно
или с помощью ранее определенного в секции Type типа.
В следующем примере массивы y, Z описаны идентично, причем y – явно, Z –
на основе ранее определенного
типа в секции Type, т. е. неявно.
Type
tA1: array [1 .. 10] of Real;
Var
y : array [1 .. 10] of Real; {массив}
Z : tA1; {массив}
Этот пример демонстрирует разные
способы описания одинаковых по структуре, но
разных по типу массивов. С точки зрения
корректного программирования он одновременно
является примером того, как не следует
описывать идентичные переменные. Причина
этого заключается в том, что идентичные
структуры во избежание непредсказуемого
поведения программы следует описывать одним
типом .
В этой связи корректным будет любой из
вариантов, приведенных в табл. 5.
Таблица 5
Корректный
неявный способ |
Корректный
явный способ |
Type tA1:
array [1 .. 10] of Real; Var Y, Z: tA1; |
Var y, Z: array
[1 .. 10] of Real; |
Многомерные
массивы содержат два и более индексов,
например :
Var h: array[1 ..3, boolean, -7 .. 7] of Word;
что эквивалентно
Var h: array[1 ..3] of
array[boolean] of array[-7 .. 7] of Word;
Для упакованных
массивов
Var packed
array[Boolean,1..10,TShoeSize] of Char;
что эквивалентно
Var
packed array[Boolean] of packed array[1..10]
of packed array[TShoeSize] of Char;
Манипуляции с
отдельными элементами массивов. Обращение к отдельному элементу
массива возможно через его индексы. В следующем
примере в секции Var описаны простая переменная i и два одномерных массива A и V как
целые переменные типа Integer. Затем
в блоке begin … end расположены три вычислительных
оператора.
Var
i: Integer;
A, V: array[1..100] of Integer;
…
begin
i:= 5;
V[8]:= i+9;
A[45]:= V[i+3]*2;
end;
При выполнении
первого из них переменная i примет значение 5. При выполнении
второго – восьмой элемент массива V примет значение 14. В третьем операторе сначала будет
вычислен индекс i + 3 = 8, затем
значение восьмого элемента массива V (значение 14) будет
умножено на 2 и полученный результат – значение 28
– будет присвоено 45-му элементу массива A.
Манипуляции с массивами. Язык
допускает с помощью одного оператора
присваивания выполнить операцию над массивом в
целом. Пусть, например, массивы A, V объявлены как квадратные матрицы.
Тогда оператор
V:= A;
выполнит копирование значений всех
элементов массива A в массив V, а после выполнения оператора
V:= A * V;
будет выполнено умножение матрицы А на
матрицу V и результат будет
помещен в матрицу V с предварительным затиранием
старых значений.
Упакованные массивы. Элементы упакованного
массива хранятся в памяти максимально плотно.
При записи он предварительно упаковывается с
целью экономии памяти. При чтении, наоборот,
распаковывается. Операции упаковки и распаковки
требуют дополнительного времени. Поэтому
использование упакованных массивов несколько
замедляет работу программы. От обычного массива Array
описание упакованного массива
отличается тем, что перед этим словом
добавляется слово Pаcked, например:
Var W: packed array [1..100] of
Integer;
7.2.
Комбинированные типы (записи)
Запись –
это объединение элементов разных типов. Как и в
массивах, следует различать запись-тип и запись-переменную.
Один элемент записи называется полем .
Запись-тип. Синтаксис записи-типа :
<имя записи> = Record
<имя поля 1> : <тип>;
<имя поля 2> : <тип>;
...
<имя поля N> : <тип>;
<вариантная часть >
End;
Записи очень
удобны для описания и хранения разнотипных данных о каких-либо однотипных
структурах.
Примером могут служить сведения о
студентах. Сведения о любом из них могут включать
поля : Фамилия, Имя,
Отчество, Год рождения, Группа, Год поступления в
вуз, Курс. Такие структуры
являются однотипными и могут быть описаны
следующим типом:
Type
TStud = Record { Сведения о студенте как запись }
Fio : String[40]; { ФИО как строка из 40 символов }
Name : String[20]; { Имя как строка из 20 символов }
Otch : String[30]; { Отчество как строка из 30 символов
}
BirthYear : Word; { Год рождения как целое типа
Word }
Group : String[8]; { Группа как строка из 8 символов }
ElectYear : Word; { Год поступления как целое
типа Word }
Curs : Byte; { Курс как целое
типа Byte }
End;
В этом
примере типы полей записи типа tStud назначены с учетом максимально
возможных значений этих полей. Так, структура
может хранить фамилию из не более чем 40 символов.
Полю Curs назначен тип Byte, который имеет диапазон
значений 0 .. 255, т. к. значением этого поля может
быть одно из значений 1 .. 6, которые полностью
охватываются диапазоном типа Byte. Этому полю
можно было бы назначить любой другой целый тип,
например Word. Однако, в целях
экономии памяти, повышения скорости чтения и
записи данных, следует назначить именно тип Byte,
который занимает всего 1 байт памяти, а не тип Word,
который требует 2 байта
памяти. В то же время, например, для поля ElectYear (год поступления) тип Byte непригоден, т. к. имеет недостаточный
диапазон значений.
Записи с вариантами. Синтаксис
записи допускает вариантность описания полей.
Вариантная часть содержит несколько
альтернатив, в каждой из которых в круглых
скобках задается список полей, присущих своему
варианту. Примером могут служить записи о
пенсионерах:
Type
tPensioner = Record {
пенсионер }
FioIO : String[100]; { Фамилия, имя, отчество одной строкой
}
Age : Byte; { Возраст }
Case Citizen: boolean of {Горожанин ли ?}
TRUE : (Town : String[30];) {Город, в котором проживает}
FALSE: (Address : String[100]; {Полный адрес одной строкой}
Transport : String[200];) {Транспорт, которым можно
добраться до города}
End;
В этом
примере запись tPensioner содержит
понятные поля FioIO и Age, а также поле нового вида –
логическое поле Citizen вариантного
типа. От значения этого поля зависит появление и
непоявление некоторых потенциальных полей
записи. Так если значение этого поля TRUE (истина), то в записи появляется
(становится доступным) поле Town (город), при значении FALSE (ложь) – поля Address
и Transport.
При использовании вариантных полей в
записях следует подчиняться следующим правилам
синтаксиса :
- Вариантная часть должна начинаться со строки, в
начале которой располагается слово
Case, а в ее конце – слово
Of. Между ними
располагается поле-признак.
- Запись должна содержать только
один вариант,
который должен располагаться в конце всех
описанных полей непосредствено перед словом End.
- Имена полей во всех вариантах должны быть разными.
Они должны также отличаться от имен полей
фиксированной части.
- Для некоторых возможных значений поля-признака
вариант может отсутствовать. В этом случае после
двоеточия, соответствующего значению варианта,
следует поставить пустой список ( ) либо не
указывать этот вариант вообще (включая значение,
двоеточие и пустое поле).
Запись-переменная. Синтаксис записи-переменной:
<имя записи> : Record
<имя поля 1> : <тип>;
<имя поля 2> : <тип>;
...
<имя поля N> : <тип>;
<вариантная часть >
End;
т.е. синтаксисы
переменной и типа отличаются одним символом (" :" и "=").
Пример :
Type
tMass : Array [1 .. 2, 1 .. 50] of Real;
tRec: Record
Name : String [10];
Mass2: tMass;
End;
Var
J: Integer;
S: String[70];
F,Gri : Record
a,b,c: Integer;
k: Array [1..10] of String [60];
z: tMass;
r: tRec;
End;
В секции Var описаны две простые переменные J и
S и две записи F и Gri, имеющих
одинаковую, но достаточно сложную структуру:
- первых три поля записей
F и Gri
имеют имена a,b,c и тип Integer;
- поле
k представляет собой
одномерный строковый массив из 10 элементов;
- поле z имеет тип, описанный в секции Type как
двумерный вещественный массив, в котором первый
индекс может изменяться в диапазоне 1
.. 2, а второй индекс – в диапазоне 1
.. 50;
- поле
r в свою очередь само
является записью, поля которой описаны типом tRec
в секции Type.
Доступ к полям
записей. Переменная, представляющая поле,
конструируется из имени записи и поля,
отделенного друг от друга десятичной точкой.
Такая составная переменная называется квалификационной .
Примеры квалификационных полей
вышеприведенных записей :
F.a Gri.a F.k[6] Gri.z [2, 34]
F.r.Name F.r.Mass2[1, 50]
Примеры операторов присваивания с
участием полей записей :
S := 'Иванов Иван
Петрович';
J := 123;
F.a := J + 9;
Gri.a := ( F.a + J ) * ( F.c + F.b - Gri.c);
Gri.a := ( F.a + J ) * ( F.c + F.b - Gri.c);
F.k [1] := F.z [2,30];
Gri.r.Name := 'Студент ' + F.k [8];
Gri.a := 12 * (Gri.a + Gri.b + Gri.c);
Доступ
к полям записей с помощью оператора With. Для упрощения обращения к полям
одной и той же записи можно использовать
оператор With.
Пример :
With Gri do
Begin
a:= 12 * (a + b + c + F.a);
b:= 64 * ( b - c);
End;
Эти операторы выполняют
те же операции, что и операторы
Gri.a:= 12 * (Gri.a + Gri.b + Gri.c + F.a);
Gri.b:= 64 * (Gri.b - Gri.c);
7.3.
Множественные типы
Множество –
это совокупность однотипных элементов. Во многом
оно похоже на типизованную константу, однако
имеет от него принципиальное отличие. Это
отличие состоит в том, что значениями множества
являются все его допустимые подмножества.
Как и в массивах, следует различать множество-тип
и множество-переменную .
Множество-тип . Синтаксис
множества-типа:
<имя множества> = Set of <базовый тип >;
Пример :
Type
TSomeInts = 1..250;
TIntSet = set of TSomeInts;
создает тип
множества с именем T IintSet,
которое содержит множество целых чисел в
диапазоне от 1 до 250. Это же множество могло быть описано
явно:
type TIntSet =
set of 1..250;
Множество-переменная.
Синтаксис множества-переменной:
<имя множества> : Set of <базовый тип >;
В соответствии с
вышеописанными типами можно объявить множества :
Var Set1, Set2:
TIntSet;
а
затем в операторной части задать эти множества:
...
Set1 := [1, 3, 5, 7, 9];
Set2 := [2, 4, 6, 8, 10];
Можно
объявить множество явно, перечислив его элементы:
Var
MySet1 : set of 'a' .. 'z';
MySet2 : set of Byte
MySet3 : set of (Club, Diamond, Heart, Spade)
MySet4 : set of Char;
...
MySet 1:= ['a','b','c']; {оператор
определения множества}
Операции над
множествами. Допустимые операции над
множествами приведены в следующей табл. 6:
Таблица 6
Опера-ция |
Наименование
операции |
Тип
операндов |
Тип
результата |
Пример |
+
–
*
<=
>=
=
<>
in |
Объединение Вычитание
Пересечение
Не меньше
Не больше
Равенство
Неравенство
Принадлежание |
set
set
set
set
set
set
set
элемент set |
set
set
set
boolean
boolean
boolean
boolean
boolean |
Set1 + Set2
S - T
S * T
Q <= MySet
S1 >= S2
S2 = MySet
MySet <> S1
A in Set1 |
Объединение,
вычитание и пересечение множеств.
Результатом любой из операций будет
также множество.
Пример :
Var
S1, S2,S3 : set of Byte;
...
S1:= [1, 2 , 3, 4]; {оператор
определения множества}
S2:= [3, 4, 5, 6, 78]; {оператор
определения множества}
S3:= S1 + S2; {объединение
множеств}
{результат S3 = [1, 2, 3, 4, 5, 6,
78] }
S3:= S2 - S1; {вычитание
множеств}
{результат S3 = [1, 2, 5, 6, 78] }
S3:= S2 * S1; {пересечение
множеств}
{результат S3 = [3, 4] }
Операции
сравнения множеств.
Результатом любой из операций будет
логическая константа True (истина)
или False (ложь).
Пример :
Var
S1, S2, S3 : set of Byte;
B: boolean;
...
S1:= [3, 4]; {оператор
определения множества}
S2:= [1, 3, 4]; {оператор
определения множества}
S3:= [3, 4, 5, 6, 78]; {оператор
определения множества}
B:= S1 <= S3; {True, т. к. S1
является подмножеством S3}
B:= S3 >= S2; {False, т. к. S2 не
является подмножеством S2}
B:= S3 = S2; {False, т. к. мн-ва S2 и
S3 не равны друг другу }
B:= S3 <> S2; {True, т. к. мн-ва
S2 и S3 не равны друг другу }
Проверка
вхождения элемента во множество. Результатом
операции in будет
логическая константа True (истина)
или False (ложь). Пример:
Var
S1 : set of Integer;
B: boolean;
...
S1:= [3, 4, 18 .. 178, 3101, 4427];
{оператор определения множества}
B:= ( 4 in S1); {True, т. к. 4
является элементом множества S1}
B:= (200 in S1); {False, т. к. 200 не
является элементом S1}
7.4. Файловые
типы
В языке Object
Pascal есть три типа файлов :
- текстовые файлы,
- файлы с типом
,
- файлы без типа.
Связь с файлом может быть установлена
через файловую переменную, которая после
описания, но до использования в программе должна
быть связана с внешним файлом с помощью
процедуры AssignFile.
Текстовой файл – это
последовательность символьных строк перемен-ной
длины. Всякая такая строка завершается маркером
конца строки CR/LF. Текстовые файлы можно
обрабатывать только последовательно. Ввод и
вывод нельзя производить для открытого файла,
используя одну файловую переменную. Текстовой
файл имеет тип TextFile, или просто Text. Пример описания файловой переменной
текстового типа:
Var Fi: TextFile;
Файлы без типа
состоят из компонент одинакового размера,
структура которых не известна или не имеет
значения. Допустим прямой доступ к любой
компоненте файла. Пример объявления файловой
переменной файла без типа :
Var F: File;
Файлы c типом состоят из
однотипных компонент известной структуры.
Допустим прямой доступ к любой компоненте файла.
Пример объявления файловых переменных для файлов
с типом:
Type
TRec = Record
A: Real;
B: Integer;
C: Char;
End;
Var
F : File of Real;
Q : File of String[100];
Fr: File of TRec;
В этом примере F
объявлена как файловая переменная вещественного
типа. Это означает, что компонентами файла могут
быть только вещественные значения. Файловая
переменная Q предназначена
для доступа к файлам, которые состоят из
символьных строк длины 100. Файловая переменная Fr
предназначена для работы с
файлами, которые состоят из записей типа TRec, объявленного в секции Type.
|