Запись с вариантной частью
Методы совмещения типов
Имя_типа (Имя_переменной)
Имя_типа (Выражение).
Функции преобразования типа определены как для стандартных, так и для пользовательских типов.
Var L: longint; W: word; | |
begin | |
integer (‘d’); | { преобразованием типа Char к типу Integer } |
byte (534); | { преобразование типа Word к типу Byte } |
... | |
W:=word (L); | { преобразование типа LongInt к типу Word } |
... | |
end. |
Различные методы совмещения типов связаны с маскированием. Маскирование позволяет изменить интерпретацию данных, содержащихся в элементах хранения объектов. Маска – это некоторый тип данных, определяющий вид интерпретации. Накладывая различные маски на одну и ту же область памяти, можно рассматривать размещенные в ней данные как принадлежащие объектам различных типов. Маскирование может быть реализовано с помощью средств, встроенных в язык программирования, или с помощью указателей. Средства языка PASCAL, реализующие совмещение типов:
¨ записи с вариантной частью,
¨ директива ABSOLUTE,
|
|
¨ параметры процедур и функций без типа,
¨ открытые массивы.
Запись с вариантной частью используется для работы с объектами различной структуры как с переменными одного и того же типа. Запись с вариантной частью состоит из одного или нескольких фиксированных полей (полей фиксированного размера) и единственной вариантной части, которая должна располагаться в конце записи.
Type
Rec =record
f1: integer;
case f2: boolean of
false: (f3, f4, f5: char);
true: (f6: word)
end;
Var r1, r2: rec;
Вариантная часть задается предложением вида:
Case селектор_вариантов of …
В конце вариантной части не следует ставить END как пару к CASE…OF. (Поскольку вариантная часть - всегда последняя в записи, за ней все же стоит END, но лишь как пара к RECORD). Селектор вариантов может быть именованным или неименованным, должен иметь любой порядковый тип (стандартный или пользовательский). Вариантная часть состоит из нескольких вариантов. Каждый вариант определяется константой выбора, за которой следует двоеточие и список полей данного варианта, заключенный в круглые скобки. Селектор варианта может принимать значение любой константы выбора.
Особенностью вариантной части является то, что все заданные в ней варианты “накладываются” друг на друга и используют одну и ту же область памяти (рис. 51). В элементе хранения конкретного объекта типа записи с вариантами всегда присутствует единственный вариант.
Рис. 51. Элементы хранения объектов, использующих запись с вариантной частью
Размер элемента хранения объектов, содержащих запись с вариантами, определяется размером самого длинного варианта:
|
|
Sizeof (Rec) = Sizeof (f1) + Sizeof (f2) + max { Sizeof (f3) + Sizeof (f4) + Sizeof (f5), Sizeof (f6) }.
Sizeof (Rec) = 6.
Таким образом, компилятор не отводит отдельную область памяти для каждого варианта, а выделяет участок памяти, достаточный для размещения варианта самого большого размера.
При использовании именованного селектора в целях дополнительного контроля следует присваивать значение селектору перед тем, как присвоить значения полям варианта, а также проверять значение селектора перед обращением к полям варианта.
Записи с вариантами применяются для описания узлов разнородных списков. Рассмотрим разнородный список, содержащий информацию о геометрических фигурах, составляющих некоторый проект (см. 4.8). Элемент хранения узла разнородного списка содержит фиксированные поля, которые располагаются в начале записи: связь в списке, координаты точки, относительно которой строится фигура. Далее рсполагается поле селектора вариантов и поля вариантов, соответствующие трем типам геометрических фигур.
Type | |||||||||||
Figure = (circle, rectangle, triangle); | { тип фигуры } | ||||||||||
PPolygon = ^ Polygon; | { указатель на эл-т хранения узла разнородного списка } | ||||||||||
Polygon = record | { эл-т хранения узла разнородного списка } | ||||||||||
link: PPolygon; | { связь в списке } | ||||||||||
x,y: word; | { координаты точки, относительно которой строится фигура } | ||||||||||
case kind: Figure of | { селектор варианта, определяющий тип фигуры } | ||||||||||
circle: (radius: word); | { окружность } | ||||||||||
rectangle: (height, width: word); | { прямоугольник } | ||||||||||
triangle: (x1,y1,x2,y2: word) | { треугольник } | ||||||||||
end; | |||||||||||
Var f: PPpolygon; | { указатель на первый узел списка } | ||||||||||
{ создание элемента хранения узла и занесение его в разнородный список } | |||||||||||
Procedure Create_Node(var first: PPolygon; t: Figure); | { t – тип фигуры } | ||||||||||
Var p: PPolygon; | { first – указатель на первый узел списка } | ||||||||||
begin | |||||||||||
new(p); | |||||||||||
writeln(‘введите координаты точки, относительно которой строится фигура’); | |||||||||||
write(‘x=’); readln(p^.x); | |||||||||||
write(‘y=’); readln(p^.y); | |||||||||||
p^.kind:=t; | { заполнение поля селектора вариантов } | ||||||||||
case t of | { заполнение полей вариантов в зависимости от типа фигуры: } | ||||||||||
circle: | { окружность } | ||||||||||
begin write(‘введите значение радиуса = ’); readln(p^.radius) end; | |||||||||||
rectangle: | { прямоугольник } | ||||||||||
begin | |||||||||||
write(‘введите значение высоты = ’); readln(p^.height); | |||||||||||
write(‘введите значение ширины = ’); readln(p^.width) | |||||||||||
end; | |||||||||||
triangle: | { треугольник } | ||||||||||
begin | |||||||||||
writeln(‘введите координаты второй вершины’); | |||||||||||
write(‘x=’); readln(p^.x1); | |||||||||||
write(‘y=’); readln(p^.y1); | |||||||||||
writeln(‘введите координаты третьей вершины’); | |||||||||||
write(‘x=’); readln(p^.x2); | |||||||||||
write(‘y=’); readln(p^.y2); | |||||||||||
end; | |||||||||||
end; | |||||||||||
p^.link:=first; first:=p; | { включение элемента хранения узла в список } | ||||||||||
end; | |||||||||||
Procedure Print(first: PPolygon); | { просмотр содержимого разнородного списка } | ||||||||||
Var p: PPolygon; | |||||||||||
begin | |||||||||||
p:=first; | |||||||||||
while p <> nil do begin | |||||||||||
writeln(‘координаты точки, относительно которой построена фигура’); | |||||||||||
writeln(‘x= ‘, p^.x, ‘y= ‘, p^.y); | |||||||||||
case p^.kind of | { проверка значения поля селектора вариантов: } | ||||||||||
circle: | { окружность } | ||||||||||
writeln(‘радиус окружности = ‘, p^.radius); | |||||||||||
rectangle: | { прямоуглоьник } | ||||||||||
writeln(‘высота прям-ка = ’, p^.height, ‘ширина прям-ка = ‘, p^.width); | |||||||||||
triangle: | { треугольник } | ||||||||||
begin | |||||||||||
writeln(‘координаты второй вершины треугольника‘); | |||||||||||
writeln(‘x= ‘, p^.x1, ‘y= ‘, p^.y1); | |||||||||||
writeln(‘координаты третьей вершины треугольника‘); | |||||||||||
writeln(‘x= ‘, p^.x2, ‘y= ‘, p^.y2); | |||||||||||
end; | |||||||||||
end; | |||||||||||
p:=p^.link | |||||||||||
end; | |||||||||||
... | |||||||||||
Procedure Destroy(var first: PPolygon); | { разрушение разнородного списка } | ||||||||||
begin | |||||||||||
... | |||||||||||
end; | |||||||||||
begin | |||||||||||
f:=nil; | { первоначально список пуст } | ||||||||||
Create_Node(f,rectangle); | |||||||||||
Create_Node(f,triangle); | |||||||||||
Create_Node(f,rectangle); | |||||||||||
Create_Node(f,circle);... | |||||||||||
Print(f);... | |||||||||||
Destroy(f); | |||||||||||
end. | |||||||||||
Неименованный селектор вариантов используется для приведения типов (память для него в элементе хранения объекта не отводится).
|
|
Type | ||
Mem = record | ||
case byte of | ||
0: (byt: array [1..4] of byte); | ||
1: (wo: array [1..2] of word); | ||
2: (l: longint) | ||
end; | ||
Var m: Mem; w: word; b: byte; | ||
begin | ||
m.l:=123456; | { инициализация переменной типа Mem значением типа LongInt } | |
b:=m.byt[4]; | { преобразование типа Mem к типу Byte } | |
w:=m.wo[1]; | { преобразолвание типа Mem к типу Word } | |
... | ||
Sizeof (Mem) = 4.
Объект m имеет три варианта, каждый из которых размещается в одном и том же элементе хранения размером 4 байта. В зависимости от того, к какому полю записи происходит обращение в программе, элемент хранения объекта m трактуется как массив из четырех байтов, массив из двух слов или длинное целое число.
5.2.2. Использование директивы ABSOLUTE
Совмещение данных в памяти может быть достигнуто при явном размещении данных разного типа по одному и тому же абсолютному адресу. Для размещения объекта по нужному абсолютному адресу после идентификатора и типа объекта следует указать стандартную директиву ABSOLUTE, за которой следует либо абсолютный адрес, либо идентификатор ранее определенного объекта. Абсолютный адрес задается парой чисел типа WORD (как правило, в шестнадцатиричном формате), разделенных двоеточием, причем первое число трактуется как сегмент, а второе – как смещение адреса.
Var b: byte absolute $0000: $0100;
Тип объекта, описанного с директивой absolute, является маской и определяет вид интерпретации данных, находящихся по заданному адресу. При определении переменной с директивой absolute фактически происходит наложение маски на область памяти по заданному адресу. Обращение к этой переменной в программе позволяет соответствующим образом интерпретировать данные, находящиеся по этому адресу.
Если за директивой absolute указан идентификатор ранее определенного объекта, то в памяти происходит совмещение данных различных типов, причем младшие байты внутреннего представления этих данных будут располагаться по одному и тому же абсолютному адресу.
Type MI = array [1..3] of integer; { маска }
Var x: real; { интерпретируемая область памяти }
y: MI absolute x; { наложение маски }
|
|
begin
x:=…; { инициализация переменной x }
writeln(y[2]); { интерпретация данных, хранящихся в переменной x,
через маску }
Eсли происходит совмещение объектов, элементы хранения которых имеют разную длину, следует размещать меньший объект по адресу большего, а не наоборот (рис.52).
Var st: string [20]; { размещение объекта меньшего размера }
stlen: byte absolute st; { по адресу объекта большего размера }
Рис. 52. Совмещение объектов с помощью директивы ABSOLUTE
Var stlen: byte; w: word; { ошибка: размещение объекта большего размера } st: string [20] absolute stlen; { по адресу объекта меньшего размера }
Запись в переменную st может испортить данные, располагающиеся за stlen.
Рассмотрим пример совмещения данных с использованием директивы ABSOLUTE. Образ текстового экрана (25 строк на 80 столбцов) в области оперативной памяти, называемой видеопамятью, начинается с адреса $B8000: $0000 и занимает 4000 байтов. Структура видеопамяти проста: видимые строки экрана хранятся последовательно. Один символ на экране занимает два байта в памяти: первый байт – ASCII код символа, второй байт – атрибут символа, задающий цвет символа, цвет фона и признак мерцания символа. Поэтому все четные адреса видеопамяти, начиная с 0, содержат символы, а нечетные – атрибуты. На образ текстового экрана в видеопамяти можно наложить маску - структуру двумерного массива, который рассчитан на 25 * 80 = 2000 элементов, включающих символы, находящиеся на экране, и их атрибуты. Использование такой маски облегчает работу с видеопамятью, т.к. позволяет получить доступ к отдельному символу и его атрибутам.
Пусть требуется установить значение текстового атрибута в видеопамяти для символа с координатами (x,y). Заметим, что координата x фактически задает номер столбца, а координата y – номер строки в двумерном массиве - маске.
Type | ||||
VideoWord= record | { образ одного символа в видеопамяти: } | |||
symbol: char; | { ASCII код символа } | |||
attr: byte | { атрибут символа } | |||
end; | ||||
{ маска – образ текстового экрана в видеопамяти - тип данных} | ||||
TextScreen = array [1..25, 1..80] of VideoWord; | ||||
{ наложение маски по абсолютному адресу в видеопамяти } | ||||
Var T: TextScreen absolute $B8000: $0000; | ||||
{ интерпретация данных, хранящихся по абсолютному адресу, через маску } | ||||
Procedure Change_Attr(x,y: byte; | { x, y – координаты символа на экране } | |||
textattr: byte); | { textattr – атрибут символа } | |||
begin | ||||
T[y,x].attr:=textattr | { установка атрибута символа с использованием маски } | |||
end; | ||||
Формальный параметр без типа внутри процедуры или функции не имеет типа и его перед использованием следует преобразовать к конкретному типу с помощью автоопределенного преобразования. Параметр без типа передается только по ссылке. Внутри процедуры или функции должна быть определена маска, задающая вид интерпретации элементов фактического параметра, передаваемого в процедуру или функцию при вызове. Напомним, что маска – это тип. Размерность маски рекомендуется выбирать так, чтобы она была рассчитана на максимальное количество данных интерпретируемого типа, которое может разместиться в пределах сегмента. (Если размер маски превышает 65520 байт, на этапе компиляции выдается сообщение об ошибке). Тип фактического параметра может быть любым, но дополнительно в процедуру или функцию следует передавать истинную размерность фактического параметра.
Пример. Просуммировать N элементов одномерных числовых массивов произвольной размерности.
... | ||||
Var | { фактические параметры - } | |||
Ar_Byte1: array[1..200] of byte; | { интерпретируемые } | |||
Ar_Byte2: array[1..2000] of byte; | { области } | |||
Ar_Int: array[1..500] of integer; | { памяти } | |||
Function Sum(var X; n: word): longint; | { X – параметр без типа, n – размерность массива } | |||
Type | ||||
Xtype =array[0..65520] of byte; | {Маска – одномерный массив типа Byte } | |||
Var | ||||
summa: longint; i: word; | ||||
begin | ||||
summa:=0; | ||||
for i:=1 to n do | ||||
{ XType(X)[i] – автоопределенное преобразование элемента массива- параметра X к типу элемента массива-маски Byte } | ||||
summa:=summa + XType(X)[i]; | { интерпретация элементов массива X через маску } | |||
Sum:=summa; | ||||
end; | ||||
begin | ||||
... | ||||
writeln(Sum(Ar_Byte1, 200)); | ||||
writeln(Sum(Ar_Int, 500)); | ||||
writeln(Sum(Ar_Byte2, 2000)); | ||||
... | ||||
В качестве первого параметра функции Sum можно использовать массив любого типа и любой размерности. В теле функции выполняется автоопределенное преобразование элемента массива-параметра X к типу элемента массива-маски Byte. Если бы в теле функции была определена маска другого типа, например, Word, преобразование выполнялось бы к этому типу.