5.3. Операция присваивания и совместимость типов и значений

We use cookies. Read the Privacy and Cookie Policy

Если в программе объявлены переменные, то подразумевается, что они будут получать свои значения по ходу ее выполнения. Единственный способ поместить значение в переменную — это использовать операцию присваивания в программе:

Переменная := Значение;

Оператор присваивания — это составной символ «:=». Его можно читать как «становится равным». В операции присваивания слева всегда стоит имя переменной, а справа — то, что представляет собой ее значение (значение как таковое или выражение либо вызов функции, но может быть и другая переменная). После выполнения присваивания переменная слева получает новое значение.

Турбо Паскаль, являясь языком с сильной системой типов, требует соблюдения определенных правил совместимости типов переменных и значений справа и слева от оператора «:=».

Очевидно, что не может быть проблем с присваиванием, если типы переменной и значений идентичны (тождественны). Два типа Type1 и Туре2 считаются идентичными, если:

1. Типы Type1 и Туре2 описаны одним и тем же идентификатором типа, например:

| TYPE

| Тype1 = Boolean;

| Type2 = Boolean;

здесь Type1 и Type2 идентичны. Но в случае

- 86 -

| TYPE

| Type1 = Array [1..2] of Boolean;

| Type2 = Array [1..2] of Boolean;

типы Type1 и Type2 не будут идентичными, поскольку конструкции Array...of..., хотя и одинаковы, но не являются идентификаторами, т.е. обособленными именами. Переменные типов Type1 и Type2 не смогут в последнем случае обмениваться значениями.

2. Типы Type1 и Type2 описаны как эквивалентные. Это означает, что, например, при описании

| TYPE

| Type1 = Array [1..2] of Boolean;

| Type2 = Type1;

| Type3 = Type2;

значения типов Type1, Type2 и Type3 будут полностью совместимы. Аналогичная картина возникает и при объявлении переменных. Если переменные причислены к одному и тому же типу

VAR

x1, x2, xЗ : Type1;

то они совместимы. Если Type1 — идентификатор типа, а не конструкция, то совместимость сохранится и при объявлении вида

| VAR

| x1 : Type1;

| x2 : Type1;

| x3 : Type2;

Здесь Type2 идентичен типу Type1, но будут несовместимы переменные x1 и x2:

| VAR

| x1 : Array [1..2] of Real;

| x2 : Array [1..2] of Real;

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

— оба типа являются одинаковыми;

— оба типа являются вещественными типами;

— оба типа являются целочисленными;

- 87 -

— один тип является поддиапазоном другого;

— оба типа являются поддиапазонами одного и того же базового типа;

— оба типа являются множественными типами с совместимыми базовыми типами;

— один тип является строковым, а другой тип — строковым или символьным типом;

— один тип является указателем (Pointer), а другой — указателем или ссылкой.

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

Существует еще один вид совместимости: совместимость по присваиванию, т.е. правила присваивания значения V2 (собственно значение, переменная или выражение) переменной V1. Они действительны только для операций присваивания и являются немногим более широкими, чем правила совместимости по типам. Значение V2 типа Type1 может быть присвоено переменной V1 типа Type2, если выполняется одно из условий:

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);

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