Параметры без типа

Запись с вариантной частью

Методы совмещения типов

Имя_типа (Имя_переменной)

Имя_типа (Выражение).

Функции преобразования типа определены как для стандартных, так и для пользовательских типов.

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, преобразование выполнялось бы к этому типу.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: