Отдельные экземпляры записей одного типа могут содержать поля, количество и структура которых зависят от значения определенной переменной. Такие поля размещают в вариантной части записи, при этом указанная переменная выполняет роль селектора.
Cинтаксис вариантной части:
Вариантная часть может содержать несколько списков полей, каждый из которых заключен в круглые скобки. Любой из списков может быть пустым, может иметь фиксированную или вариантную части, а также и фиксированную, и вариантную части. Идентификатор - это селектор выбора полей вариантной части.
Пример 1.
Предположим, что нам требуется создать электронную картотеку публикаций по определенной теме. Будем считать, что такими публикациями могут быть книги или статьи из журналов (в общем случае публикациями могут быть также статьи из сборников, из газет, из реферативных журналов и др.).
Составим следующую структуру данных:
Публикация (Publication)
Книга (Book)
Автор (Author)
Название (Title)
Год (Year)
Издательство (PublishingHouse)
Кол-во страниц (Pages)
Статья (Article)
Автор (Author)
Название (Title)
Год (Year)
Журнал (Journal)
Номер журнала (NumberOfJournal)
Начальная страница (BegPage)
Конечная страница (EndPage)
Одинаковыми в каждом из видов публикаций являются три параметра - автор, название и год. Эти параметры следует вынести в фиксированную часть записи, остальные - в вариантную часть.
В приводимой ниже программе формируется массив записей о публикациях. Для каждой записи вначале запрашивается вид публикации, а затем вводятся параметры, относящиеся к данному виду.
Program VarRecord;
Type KindOfPublication = (Book,Article);
Publication = record
Author : string[40];
Title : string;
Year : word;
Case Kind : KindOfPublication of
Book : ( PublishingHouse : string;
Pages : word );
Article : ( Journal : string;
NumberOfJournal : byte;
BegPage,EndPage : word );
end;
PublicationArray = array[1..500] ofPublication;
Vari,k : byte;
NumPublic : word;
Pub : Publication;
Publis : PublicationArray;
Begin
NumPublic:=0;
Repeat
Write('Вид публикации (0 - Книга, 1 - Статья,',
' >1 - Конец ввода) = ');
Readln(k);
Ifk<2 then
Begin
With Pub do
Begin
Kind:=KindOfPublication(k);
Write(' Автор : '); Readln(Author);
Write(' Название : '); Readln(Title);
Write(' Год : '); Readln(Year);
Case Kind of
Book : Begin
Write(' Издательство : ');
Readln(PublishingHouse);
Write(' Кол-во страниц : ');
Readln(Pages);
End;
Article : Begin
Write(' Название журнала : ');
Readln(Journal);
Write(' Номер журнала : ');
Readln(NumberOfJournal);
Write(' Страницы (начальная, ',
'конечная : ');
Readln(BegPage,EndPage);
End;
end { Case };
End{ With };
Inc(NumPublic);
Publis[NumPublic]:=Pub;
End { If };
Until k>1;
End.
Переменная Kind имеет тип KindOfPublication; это переменная перечисляемого типа, принимающая согласно ее объявления одно из двух возможных значений: Book или Articles (Book, Articles - это не строки, а имена констант, которым на машинном уровне соответствуют значения 0 и 1). Так как операции ввода-вывода по отношению к перечисляемым переменным недопустимы, то в программе VarRecord вводится значение вспомогательной переменной k типа byte, которая интерпретируется как порядковый номер имени константы в списке KindOfPublication:
Readln(k); Kind:=KindOfPublication(k);
Здесь используется аппарат приведения типов переменных: переменная k трактуется как внутримашинное значение переменной типа KindOfPublication.
При обработке массива Publis вначале, очевидно, нужно читать в очередной записи вид публикации, а затем поля, относящиеся к этому виду. Например, вывод на экран содержимого массива Publis можно организовать следующим образом:
Fori:=1 toNumPublic do
Begin
k:=byte(Publis[i].Kind);
Case k of
0 : Writeln(' К н и г а :');
1 : Writeln(' С т а т ь я :');
end;
Write('Автор: '); Writeln(Publis[i].Author);
Write('Название: '); Writeln(Publis[i].Title);
Write('Год издания: '); Writeln(Publis[i].Year);
Case k of
0 : Begin
Write('Издательство: ');
Writeln(Publis[i].PublishingHouse);
Write('Кол-во страниц: ');
Writeln(Publis[i].Pages);
End;
1 : Begin
Write('Журнал: ');
Writeln(Publis[i].Journal);
Write('Номер журнала: ');
Writeln(Publis[i].NumberOfJournal);
Write('Начальная страница: ');
Writeln(Publis[i].BegPage);
Write('Конечная страница: ');
Writeln(Publis[i].EndPage);
End;
end;
End;
Для вариантной части записи выделяется столько памяти, сколько занимает ее наибольший список полей. При этом селектор размещается непосредственно после фиксированной части записи.
Определим объем памяти, выделяемой для записи Pub.
Поле Author - 41 байт;
" Title - 256 байт;
" Year - 2 байта;
Селектор Kind - 1 байт;
Вариант Book:
Поле PublishingHouse - 256 байт;
" Pages - 2 байта;
---------------------------------
Всего - 258 байт;
Вариант Article:
Поле Journal - 256 байт;
" NumberOfJournal - 1 байт;
" BegPage - 2 байта;
" EndPage - 2 байта.
---------------------------------
Всего - 261 байт.
Следовательно, в целом для записи Pub будет выделено 41+256+2+1+261 = 561 байт.
В вариантной части каждого элемента массива записей Publis размещается или вариант Book, или вариант Article. Интерпретация структуры вариантной части при обработке в программе k-го элемента массива Publis определяется значением переменной Kind, входящей в состав этого элемента.
Рассмотрим структуру записи Pub.
При Kind = Book (k = 0):
½¾¾¾¾¾¾¾¾¾¾¾¾¾ Pub, 561 байт ¾¾¾¾¾¾¾¾¾¾¾¾®½
Autor
Title
Year
Kind
PublishingHouse
Pages
41 байт 256 2 1 256 2 3
Здесь последние 3 байта поля памяти Pub не используются.
Адреса полей записи Pub:
Pub.Autor = Pub + 0
Pub.Title = Pub + 41
Pub.Year = Pub + 297
Pub.Kind = Pub + 299
Pub.PublishingHause = Pub + 300
Pub.Pages = Pub + 556
При Kind = Articles (k = 1):
½¾¾¾¾¾¾¾¾¾¾¾¾¾¾ Pub, 561 байт ¾¾¾¾¾¾¾¾¾¾¾¾®½
Autor
Title
Year
Kind
Journal
NumberOfJournal
BegPage
EndPage
41 байт 256 2 1 256 1 2 2
Адреса полей записи Pub:
Pub.Autor = Pub
Pub.Title = Pub + 41
Pub.Year = Pub + 297
Pub.Kind = Pub + 299
Pub.Journal = Pub + 300
Pub.NumberOfJournal = Pub + 556
Pub.BegPage = Pub + 557
Pub.EndPage = Pub + 559
Вне зависимости от значения переменной Kind имена Pub.Pages и Pub.NumberOfJournal определяют один и тот же адрес оперативной памяти, равный Pub+556, но в первом случае - это адрес поля памяти длиной 2 байта, а во втором случае - длиной 1 байт. Следовательно, для четкой идентификации полей в вариантной части записи имена этих полей должны быть уникальными, в том числе и в случае, когда эти поля относятся к разным вариантам. Это означает, что имена полей должны отличаться друг от друга хотя бы одним символом.
Слово Case в объявлении вариантной части записи не имеет соответствующего ему слова End, как в операторе Case. Поскольку вариантная часть всегда стоит последней в записи, то в данном случае слово Endявляется общим для слов Case и Record.
Селектор в вариантной части, в сответствии с ее синтаксисом, является необязательным. При его отсутствии имя типа после слова Case определяет лишь тип константы варианта. Обращение к полям вариантов производится просто по именам этих полей. В этом случае вариантную часть можно рассматривать как различную интерпретацию одного и того же поля памяти, аналогично аппарату абсолютных переменных.
Пример 2.
Program DemoRecord;
Type DemoRec = record
A : real;
Case integer of
1 : (B,C,D : word);
2 : (BL,BH,CL,CH : byte);
end;
VarRec : DemoRec;
Begin
Rec.B:=260;
Rec.CL:=1;
Rec.CH:=2;
Writeln('Rec.B=',Rec.B,' Rec.BL=',Rec.BL,
' Rec.BH=',Rec.BH);
Writeln('Rec.C=',Rec.C,' Rec.CL=',Rec.CL,
' Rec.CH=',Rec.CH);
End.
Следует обратить еще раз внимание на то, что символы "1" и "2" в объявлении вариантной части записи DemoRec - это не метки, а константы типа integer.
В программе DemoRecord будет отпечатано:
Rec.B=260 Rec.BL=4 Rec.BH=1
Rec.C=513 Rec.CL=1 Rec.CH=2
26010 = 10416 = 00000001 000001002
51310 = 20116 = 00000010 000000012
Как известно, в машинном изображении двухбайтное число располагается таким образом, что старшим при этом является правый байт, а биты в каждом байте нумеруются справа налево. Двухбайтное поле B и два однобайтных поля BL и BH - это одно и то же поле памяти:
½¾¾¾¾¾¾¾¾¾ Rec.B ¾¾¾¾¾¾¾¾¾¾¾®½
½¾¾¾¾ Rec.BL ¾¾¾®½¾¾¾¾ Rec.BH ¾¾¾®½
Аналогичное соотношение имеет место между полями С и CL, CH.