Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Turbo Pascal / Stud_1_1 / LecRus / MainPart.doc
Скачиваний:
116
Добавлен:
03.03.2016
Размер:
5.03 Mб
Скачать

Тождественные и совместимые типы

При передаче и преобразовании информации в программе должны соблюдаться некоторые требования, блокирующие некорректные действия программиста. Эти требования могут быть разделены на три группы, соответствующие следующим случаям:

  • передача фактического параметра-переменной;

  • вычисление выражения;

  • выполнение оператора присваивания и передача фактического параметра-значения.

В первом случае должны быть выполнены требования тождественности типов, во втором – совместимости типов, в третьем – совместимости по присваиванию.

Тождественность типов.

Рассмотрим процедуру, в списке формальных параметров которой заданы лишь параметры-переменные (перед именем формального параметра записано слово Var).

Пример 1.

Type Ar1 = array[1..100] of real;

Ar2 = array[1..100] of real;

Ar3 = Ar1;

Ar4 = Ar3;

Var X : Ar1; Y : Ar2; Z : Ar3; W : Ar4;

a,b : real;

m,n : integer;

Procedure Proc1(Var D:Ar1; Var k:integer);

Begin

..........................

End { Proc1 };

Ранее было указано, что формальный и соответствующий ему фактический параметр должны иметь одно и то же имя типа. Это не совсем точно.

Обозначим через Type1 имя типа формального параметра, через Type2 - имя типа соответствующего ему фактического параметра.

Как известно, при обращении к процедуре фиктивный адрес формального параметра-переменной замещается реальным адресом фактического параметра. В этом случае формальная и фактическая переменные соответствуют одному и тому же полю памяти. Следовательно, имена типов Type1 и Type2 этих переменных должны определять переменные одинакового размера и структуры, с одинаковым множеством допустимых значений и операций по их обработке. Последнее возможно, если типы Type1 и Type2 тождественны.

Два типа считаются тождественными, если они представляют собой одно и то же имя типа или один из них описан как эквивалентный другому типу.

В примере 1 типы Ar1, Ar3 и Ar4 тождественны, Ar1 и Ar2 - не тождественны, хотя они и имеют одинаковое описание типа. Поэтому обращения к процедуре Proc1(X,m), Proc1(Z,m) и Proc1(W,m) считаются правильными, а при обращении Proc1(Y,m) будет выведено сообщение "Type mismatch" ("Несоответствие типов").

Примечание. Вполне очевидно, что сказанное выше в равной мере относится также к параметрам-константам.

Совместимость типов. Рассмотрим пример 2.

Пример 2.

Var x,y : real;

m,n : integer;

ch : char;

b : boolean;

Begin ...............

y:=x+2*ch;

b:=(x<ch) and n;

Вполне очевидно, что выражения в правой части операторов присваивания не могут быть вычислены, поскольку лишены смысла арифметические операции по отношению к символьным переменным, сравнение числовых и символьных переменных и т.п. В этих выражениях нарушены требования совместимости типов.

Основные правила совместимости типов:

- операнды выражения имеют численные типы (вещественный, целочисленный, диапазонный);

- операнды определены логическим типом;

- операнды имеют строковый, символьный или диапазонный символьный типы.

Если операнды выражения имеют различные численные типы, то при выполнении арифметических операций производится преобразование их значений к более старшему типу в соответствии со следующими приоритетами:

- real;

- longint;

- integer, word;

- shortint, byte.

Совместимость типов по присваиванию. Рассмотрим пример 3.

Пример 3.

Var x,y : real;

m,n : integer;

ch : char;

b : boolean;

Begin ...............

{ 1 } y:=(m<n) and b;

{ 2 } ch:=x+m;

{ 3 } m:=2*x+y;

{ 4 } y:=3*m-n;

Числовой переменной нельзя присвоить булевское значение, символьной переменной - численное значение. Поэтому операторы 1 и 2 не могут быть выполнены, здесь нарушается требование совместимости по присваиванию.

В правой части оператора 3 - вещественное значение, в левой части - целочисленная переменная. Если допустить выполнение такого оператора, то дробная часть вещественного значения должна быть отброшена, т.е. произошла бы потеря точности. В связи с этим считается, что в операторе 3 также нарушаются требования совместимости по присваиванию.

В операторе 4 потеря точности не наблюдается. При его выполнении производится лишь преобразование целочисленного значения, полученного при вычислении выражения в правой части оператора, к типу real.

Обозначим тип переменной в левой части Type1, тип значения выражения - Type2.

Основные требования совместимости по присваиванию:

- Type1 и Type2 имеют тождественные типы и ни один из них не является файловым типом;

- Type1 и Type2 - целочисленные типы;

- Type1 - вещественный тип, Type2 - вещественный или целочисленный тип;

- Type1 и Type2 - строковые типы;

- Type1 - строковый тип, Type2 - символьный тип.

Более жесткие требования должны соблюдаться, когда в левой и правой частях оператора присваивания записаны составные переменные, в частности, массивы.

Пример 4.

Type Ar1 = array[1..100] of real;

Ar2 = array[1..100] of real;

Ar3 = Ar1;

Var X,Y : Ar1; Z : Ar2; W : Ar3;

Здесь типы Type1 и Type2 должны быть тождественными. Следовательно, в этом случае операторы Y:=X и Y:=W являются допустимыми, в то время как для оператора Y:=Z будет определено несоответствие типов.

Рассмотрим теперь процедуру, в списке формальных параметров которой имеются параметры-значения.

Пример 5.

Var x,y : real;

m,n : integer;

Procedure Proc2(k:integer; r,t:real);

Begin

.............

End { Proc2 };

Begin

.............

{ 1 } Proc2(m,n-1,x+m);

{ 2 } Proc2(y,x+y,x);

Для параметра-значения в теле процедуры выделяется поле памяти в соответствии с его типом. При обращении к процедуре в это поле пересылается значение фактического параметра; другими словами, формальному параметру присваивается значение фактического параметра. Следовательно, по отношению к параметру-значению должны соблюдаться изложенные выше требования совместимости по присваиванию. В частности, в примере 5 для оператора 1 эти требования соблюдаются, для оператора 2 - не соблюдаются.

П Р И В Е Д Е Н И Е Т И П О В П Е Р Е М Е Н Н Ы Х

Имя любой переменной, объявленной в разделе Var, - это адрес поля памяти в машинной программе. Оператор присваивания a:=b, рассматриваемый на уровне машинной программы, - это пересылка содержимого поля памяти b в поле памяти a.

При программировании на машинном языке или на языке ассемблера произвольному полю памяти можно присвоить значение любого другого поля памяти. В этом случае ответственность за правильность выполнения оператора присваивания полностью возлагается на программиста.

Иная ситуация имеет место при программировании на языках высокого уровня, в частности на языке Паскаль. Имя переменной, идентифицирующей адрес поля памяти, всегда имеет вполне определенный тип. Тип переменной однозначно определяет множество значений, которые имеет право принимать переменная данного типа, и набор операций, которые допустимы при ее обработке.

Пример.

Var k,m : byte;

p,q : char;

Begin

k:=65; p:='A';

1) m:=k; q:=p;

2) m:=p; q:=k;

3) m:=ord(p); q:=chr(k);

Внутреннее представление переменной k и переменной p совершенно одинаково (порядковый номер символа 'A' в таблице ASCII равен 65) и имеет вид 0110 0101.

Действие оператора m:=k сводится к пересылке содержимого однобайтного поля k в поле m. Аналогичные действия выполняются для оператора q := p. В то же время операторы m:=p и q:=k не могут быть выполнены, так как при их трансляции будет обнаружено несоответствие типов.

Пересылка содержимого поля p в поле m может быть выполнена оператором m:=ord(p). Функция ord не производит никаких преобразований значения переменной p, она лишь информирует транслятор, что значение этой переменной следует рассматривать как значение целочисленной переменной и переслать соответственно в поле m. Аналогичная ситуация имеет место для оператора q:=chr(k). Следовательно, функции ord и chr выполняют приведение типа, а не преобразование значений переменных.

При программировании на Паскале может возникать необходимость рассматривать одно и то же поле памяти как значения переменных самых различных типов, а не только типов char и byte. В этом случае используют аппарат приведения типа переменных, с помощью которого обращение к переменной одного типа может рассматриваться как обращение к переменной другого типа.

Синтаксис приведения типа:

Здесь поле памяти, определяемое идентификатором переменной, рассматривается как экземпляр типа, представленного идентификатором типа.

Рассмотрим суть приведения типов в Турбо Паскале на следующих трех примерах.

Пример 1.

Var ch : char;

b1,b2 : byte;

Begin

ch:='A'; b1:=byte(ch); b2:=ord(ch);

Writeln('b1=',b1,' b2=',b2);

Будет отпечатано:

b1=65 b2=65

Выражение byte(ch) определяет "наложение" типа byte на переменную ch. В этом случае биты, содержащиеся в поле памяти ch, интерпретируются как значение переменной типа byte (ch = 010000012 = 4116 = 6510).

Примечание. Внешняя форма записи выражения byte(ch) и функции ord(ch) одинакова, что позволяет трактовать выражение byte(ch) как функцию приведения к типу byte.

Пример 2.

Type ByteAr = array[1..2] of byte;

Var W : word;

Begin

ByteAr(W)[1]:=10; ByteAr(W)[2]:=20;

Writeln(W,' ',ByteAr(W)[1],' ',ByteAr(W)[2]);

W:=2500;

Writeln(W,' ',ByteAr(W)[1],' ',ByteAr(W)[2]);

В результате работы программы будет отпечатано:

5130 10 20

2500 196 9

B примере 2 происходит наложение "формального" поля ByteAr длиной два байта на реальное поле W, также имеющее длину два байта. При этом байты, входящие в состав поля W, рассматриваются как отдельные элементы байтового массива типа ByteAr.

Отпечатанные выше элементы в 16 с/с имеют следующие значения:

140A OA 14

09C4 C4 09

Cоответствие между элементами полей W и ByteAr легко просматривается, если учесть, что в переменной типа word старшим считается правый байт. Последнее определяется тем, что в процессорах типа Intel числовое значение размещается в поле памяти таким образом, что более старшие разряды числа располагаются в байтах с более старшими адресами. Это положение иллюстрируется ниже на примере переменной k типа longint.

Переменной k выделяется 4 байта памяти. Поскольку адресом поля памяти является адрес его крайнего левого байта, то байты поля k получат следующие адреса:

k + 0 k + 1 k + 2 k + 3

Пусть k = 2 000 100 101 = $ 77 37 1B 05. Тогда в байтах с адресами k+0 .. k+3 шестнадцатеричные цифры значения переменной k будут расположены следующим образом:

k + 0 k + 1 k + 2 k + 3

05

1B

37

77

Приведение типа T(v), где T - имя типа, v - имя переменной, позволяет трактовать на машинном уровне биты, содержащиеся в поле v, как биты, принадлежащие значению типа T.

Пример 3. Определить численные значения байтов, входящих в состав полей памяти типов integer и real. Здесь фактически идет речь об определении внутренного представления переменных типов integer и real.

Program PutType;

Type ByteAr2 = array[1..2] of byte;

ByteAr6 = array[1..6] of byte;

Var I : integer;

R : real;

Begin

I:=5000; R:=5000;

Writeln(' I: ',ByteAr2(I)[1],' ',ByteAr2(I)[2]);

Writeln(' R: ',ByteAr6(R)[1],' ',ByteAr6(R)[2],

' ',ByteAr6(R)[3],' ',ByteAr6(R)[4],

' ',ByteAr6(R)[5],' ',ByteAr6(R)[6]);

I:=-5000; R:=-5000;

Writeln(' I: ',ByteAr2(I)[1],' ',ByteAr2(I)[2]);

Writeln(' R: ',ByteAr6(R)[1],' ',ByteAr6(R)[2],

' ',ByteAr6(R)[3],' ',ByteAr6(R)[4],

' ',ByteAr6(R)[5],' ',ByteAr6(R)[6]);

End.

Результаты работы программы удобно представить в следующем виде:

I: 136 19  I: 88 13

R: 141 0 0 0 64 28  R: 8D 00 00 00 40 1C

I: 120 236  I: 78 EC

R: 141 0 0 0 64 156  R: 8D 00 00 00 40 9C

Здесь в левой части - печатаемые программой результаты (в десятичной системе счисления), в правой части - те же результаты в шестнадцатеричной системе счисления.

Проверка результатов:

5000 = $1388 ; -5000 = $EC78 ;

1388 = 0,1388  164 = 0, 0001 0011 1000 1000  216 = 0,1001110001000  213

Значение R в 16 с/c : 1C 40 00 00 00 8D.

Полученные результаты четко показывают, что числовые данные располагаются в памяти таким образом, чтобы более старшие разряды находились в байтах с адресами большей величины.

Пусть для вещественной переменной R компилятор отвел поле памяти с адресом A. Так как переменная типа real занимает 6 байтов, то в поле R входят байты с адресами A+0, A+1, A+2, A+3, A+4, A+5. Тогда для R=5000 байты будут иметь следующее содержимое:

(A+0) = 8D ; (A+1) = 00 ; (A+2) = 00 ; (A+3) = 00 ; (A+4) = 40 ; (A+5) = 1C .

Пример 4.

Var x : real;

y : byte;

Вместо условного оператора

If x<0 then

y:=1

Else

y:=0;

можно написать

y:=byte(x<0);

А Б С О Л Ю Т Н Ы Е П Е Р Е М Е Н Н Ы Е

Как известно, память для переменных, объявленных в разделе Var, выделяется при старте блока, в котором содержится данный раздел, в порядке объявления имен переменных. Местоположение блока в пространстве оперативной памяти ЭВМ определяет операционная система. В то же время Турбо Паскаль предоставляет возможность программисту так описать переменные, чтобы им были назначены заранее заданные адреса. Такие переменные называются абсолютными; их описание после имени типа должно содержать так называемое абсолютное предложение.

Синтаксис абсолютного предложения:

Структура абсолютного предложения имеет два альтернативных варианта.

В первом варианте производится задание абсолютного адреса в форме, принятой в операционной системе MS DOS (адрес сегмента и смещение - см.раздел "Адресация операндов").

Пример 1.

Type ScreenChar = record

Symbol : char;

Attrib : byte;

end;

ScreenAr = array[1..25,1..80] of ScreenChar;

Var y : real absolute $A100:$0000;

Screen : ScreenAr absolute $B800:$0000;

Здесь переменным y и Screen назначаются конкретные адреса оперативной памяти. В частности, значение $B800:$0000 определяет фиксированный адрес видеопамяти.

Примечание. В конструкции record .. end объединяются переменные различного типа (см.раздел "Записи").

Во втором варианте для абсолютной переменной назначается адрес другой, ранее объявленной переменной.

Пример 2 (аналогично примеру 2 раздела "Приведение типов переменных").

Type ByteAr = array[1..2] of byte;

Var W : word;

Br : ByteAr absolute W;

Begin

Br[1]:=10; Br[2]:=20;

Writeln(W,' ',Br[1],' ',Br[2]);

W:=2500;

Writeln(W,' ',Br[1],' ',Br[2]);

В результате работы программы будет отпечатано:

5130 10 20

2500 196 9

Переменные W и Br адресуют одно и то же поле памяти. Другими словами, одно и то же поле памяти размером два байта имеет два имени: имя W и имя Br. Если в программе используется имя W, то это поле можно обрабатывать как переменную типа word, для имени Br допустимые операции определяются именем типа ByteAr.

При совмещении переменных с помощью директивы absolute не рекомендуется в разделе Var использовать описание типа вместо имени типа. Нарушение этого правила не анализируется компилятором, но может привести к неправильной работе программы, вплоть до ее "зависания", особенно при использовании сопроцессора.

Например, вместо описания

Var A : array[1..6] of longint;

B : array[1..4] of real absolute A;

следует применять описание

Type ArLong = array[1..6] of longint;

ArReal = array[1..4] of real;

Var A : ArLong;

B : ArReal absolute A;

Соседние файлы в папке LecRus