Выбрать главу

1. Type1 и Type2 — тождественные типы, и ни один из них не является файловым типом или структурным типом, содержащим компонент с файловым типом.

2. Type1 и Type2 — совместимые перечислимые типы, и значения типа Type2 попадают в диапазон возможных значений Type1.

3. Type1 и Type2 — вещественные типы, и значения типа Type2 попадают в диапазон возможных значений Typel.

4. Typel — вещественный тип, а Type2 — целочисленный тип.

5. Type1 и Type2 — строковые типы.

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

7. Type1 и Type2 — совместимые множественные типы, и все члены значения множества типа Type2 попадают в диапазон возможных значений Type1.

8. Type1 и Type2 — совместимые адресные типы.

9 Тип объекта Type2 совместим по присваиванию с типом объекта Type1, если Type2 находится в области типа объекта Type1.

10. Тип ссылки Ptr2, указывающий на тип объекта Type2, совместим по присваиванию с типом ссылки Ptr1, указывающим на тип объекта Type1, если Type2 находится в области типа объекта Type1.

- 88 -

Последние два правила, относящиеся к данным типа «объект», не слишком очевидны. Более подробное их описание приводится в гл. 13 «Объектно-ориентированное программирование».

Нарушение правил совместимости типов и значений обнаруживается, как правило, на этапе компиляции программы.

С вопросом совместимости очень тесно связан вопрос о типе результатов арифметических выражений. Например, можно ли заранее сказать, какой будет тип у результата выражения справа?

| VAR

| B :Byte;

| W : Word;

| I : Integer;

| R : Real;

...

| R := В*I+W;

На этот счет существуют четкие правила внутреннего преобразования типов значений — участников операций:

1. В случае бинарной операции, использующей два операнда, оба операнда преобразуются к их общему типу перед тем, как над ними совершается действие. Общим типом является встроенный целочисленный тип с наименьшим диапазоном, включающим все возможные значения обоих типов. Например, общим типом для целого и целого длиной в байт является целое, а общим типом для целого и целого длиной в слово является длинное целое. Действие выполняется в соответствии с точностью общего типа, и типом результата является общий тип. Если же один из операндов — вещественный, а второй — целочисленный, то результатом операции может быть только значение вещественного типа.

2. Выражение справа в операторе присваивания вычисляется независимо от размера переменной слева.

Если результат выражения «не вписывается» в тип переменной слева от знака «:=», то может возникнуть ошибка переполнения. В случае вещественной переменной слева при переполнении возникнет ошибка счета 205 (Floating Point overflow). Но если слева стоит целочисленная переменная, то при режиме компиляции {$R+} возникнет ошибка нарушения диапазона 201 (Range Check Error), а при {$R-} программа не прервется, но значение в переменной будет «обрезано» ее диапазоном и перестанет соответствовать выражению справа. Последний случай чреват труднодиагностируемыми ошибками в результатах счета программы.

- 89 -

Другим примером опасности может стать вывод значений выражений оператором Write (или в общем случае подстановка выражений в вызовы процедур или функций):

| VAR

| A, B : Word;

| BEGIN

| A:= 55000;

| B:= A-256;

| Write(A+B);

| END.

Эта программа должна вычислить значение A+B и вывести его на экран. Но она этого не сделает. В режиме компиляции {$R+} запуск программы даст ошибку 201, поскольку общий для A и B тип Word не вмещает их сумму (его «потолок» равен 65535). В режиме {$R-} программа напечатает заведомую ложь.

Выход из подобных ситуаций прост. Надо объявлять хотя бы одного участника выражения более длинным (емким) типом. Так, если описать A как LongInt, то общим для A и B типом станет LongInt, и в нем уместится достаточно большое значение суммы. Можно даже просто, не изменяя объявления переменной, переписать последний оператор в виде

Write(LongInt(A) + B);

используя описываемое ниже приведение типа значения.

5.4. Изменение (приведение) типов и значений

В Турбо Паскале имеется очень мощное средство, позволяющее обойти всевозможные ограничения на совместимость типов или значений: определена операция приведения типа. Она применима только к переменным и значениям. Суть этой операции в следующем. Определяя тип, мы определяем форму хранения информации в ОЗУ, и переменная данного типа будет представлена в памяти заранее известной структурой. Но если «взглянуть» на ее образ в памяти с точки зрения машинного представления другого типа, то можно будет трактовать то же самое значение как принадлежащее другому типу. Для этого достаточно использовать конструкцию

ИмяТипа( ПеременнаяИлиЗначение )

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

- 90 -

| TYPE

| Arr4Byte = Array[1..4] of Byte; { массив из 4-х байтов }

| Arr2Word = Array[1..2] of Word; { массив из двух слов }

| RecType = RECORD

| Word1, Word2 : Word { запись из двух слов }

| END;

| VAR

| L : LongInt; { четырехбайтовое целое со знаком }

| S : ShortInt; { однобайтовое целое со знаком }

| В : Byte; { однобайтовое целое без знака }

| W : Word; { двухбайтовое целое без знака }

| a4 : Arr4Byte; { массив из четырех байтов }

| a2 : Arr2Word; { массив из двух слов по два байта }

| Rec : RecType; { запись из двух слов по два байта }

| BEGIN

| L := 123456; { некое значение переменной L }

| S := -2; { некое значение переменной S }

| a2 := arr2Word( L ); { два слова L перешли в массив a2 }

| a4 := arr4Byte( L ); {четыре байта L перешли в a4 }

| W := RecType( L ).Word1; { доступ к L по частям

| W := arr2Word( L )[ 1 ];

| RecType(L).Word1 := 0; { обнуление первого слова в L }

| B := Byte( S ); { если S=-2, то B станет 254 }

| B := Arr4Byte( a2 )[1]; { запись в B значения первого }

{полуслова массива a2 }

| END.

Приведение типов не переопределяет типы переменных. Оно лишь дает возможность нарушить правила совмещения типов при условии, что соответствующие значения совместимы в машинном представлении. При преобразовании типа переменной ее размер всегда должен быть равен размеру типа, к которому приводится значение. Если после приведения переменной к структуре мы хотим воспользоваться отдельным ее элементом, то просто приписываем индекс (для массивов) или поле (для записей). Вообще говоря, конструкцию ИмяТипа( Переменная ) можно условно считать именем некой необъявленной переменной типа «ИмяТипа» и со значением, хранимым в «Переменной». Приведение типа переменной может стоять как слева, так и справа, т.е. может участвовать в выражениях. Допускается вложенность преобразований при условии сохранения размеров. Как видно из примера, можно изменять определенные байты общей структуры переменной в памяти независимо от ее типа.

- 91 -

Аналогично изменению типа переменных можно изменять тип значений как таковых, а также результирующий тип выражений, т.е. разрешены такие преобразования:

Integer( 'Y' ) { код символа 'Y' в формате Integer }

Boolean( 1 ) { это логическое значение True }

LongInt( 1 ) { значение 1, размещенное в четырех байтах}

Char ( 130-1 ) { символ с кодом ASCII номер 129 }

В этом случае происходит трактование значения в скобках как значения другого типа. Например, символ 'Y' хранится как код 89 и занимает один байт. Но конструкция Integer ('Y') представляет собой значение 89 в формате целого числа со знаком (в двух байтах). Как видно, при преобразовании типа значений соблюдение размера уже не нужно. Новый тип может быть шире, а может быть и короче, чем естественный тип значения.