Всякий раз, когда в операторе используются два операнда, над одним из них или
над обоими производится некоторый род преобразований. Например,
short int
и long int
нельзя складывать напрямую.
Вместо этого short int
должен быть преобразован в long
int
, и, затем, два значения могут быть сложены.
К счастью, C производит большинство преобразований неявно. Простого указания, что два значения должны быть сложены, достаточно, чтобы компилятор C проверил их типы и сгенерировал соответствующие преобразования. Однако иногда необходимо точно знать, как C будет преобразовывать операнды.
Преобразование операндов происходит с попыткой сохранить значения операнда. Там, где невозможно сохранить значение, компилятор будет производить расширение знака знаковых величин и отбрасывать старшие биты у величин, конвертируемых к меньшим типам.
Правила преобразования типов полностью раскрыты в последующих разделах.
Правило:
char
, short int
или битовое поле
типа int
в любой, знаковой или беззнаковой форме, или объект
перечислимого типа всегда преобразуются в int
. Если тип
int
недостаточен для хранения всего диапазона преобразуемого
объекта, то он конвертируется в unsigned int
.
signed
или unsigned char
всегда конвертируется в
signed int
без изменения значения.
В Watcom C/16 short int
имеет тот же диапазон, что
и int
, поэтому signed short int
конвертируется в signed int
, а unsigned
short int
— в unsigned int
без изменения
значения.
В Watcom C/32 signed
или unsigned short
int
конвертируется в int
без изменения
значения.
Это преобразование называется целочисленным расширением.
Правило:
Если беззнаковое целое конвертируется в целое любого размера,
то, если значение может быть представлено новым типом, то оно остаётся без
изменений.
Если беззнаковое целое конвертируется в более длинный тип (тип с большим диапазоном), то значение не изменится. Если оно преобразуется к типу с меньшим диапазоном и может быть этим меньшим диапазоном представлено, то значение останется без изменения. Если значение не может быть представлено и, если результат имеет знаковый тип, то конечный результат зависит от реализации. Если тип результата — беззнаковый, то результат — целое по модулю (1 + наибольшее беззнаковое число, которое можно хранить в этом более коротком типе).
В Watcom C/16 беззнаковые целые приводятся к более длинным типам заполнением старших битов нулями. Приведение к меньшим типам производится отбрасыванием старшей части большего типа.
Рассмотрим следующие примеры 32-битных величин (unsigned long
int
), которые сконвертированы к 16-битным (signed short
int
или unsigned short int
):
long |
32-битное представление |
16-битное представление |
signed short |
unsigned short |
---|---|---|---|---|
65538 | 0x00010002 | 0x0002 | 2 | 2 |
100000 | 0x000186A0 | 0x86A0 | -31072 | 34464 |
Правило:
Если знаковое целое конвертируется к беззнаковому такой же
или большей длины и значение неотрицательно, то значение остаётся без
изменений.
Неотрицательное значение, хранимое в знаковом целом, может быть сконвертировано в эквивалентный или более длинный целый тип без влияния на значение. Отрицательное значение сначала конвертируется в знаковый тип такой же длины, что и результат, а затем к нему добавляется (1 + наибольшее беззнаковое число, которое может быть сохранено в результирующем типе), чтобы сконвертировать его к беззнаковому типу.
В Watcom C/16 знаковые целые приводятся к более длинным типам при помощи расширения знака (старший бит более короткого типа заполняет все старшие биты более длинного). Если более длинный тип — беззнаковый, то расширяемый знаковый бит затем считается беззнаковым значением.
Рассмотрим следующие примеры 16-битных величин (signed short
int
), которые сконвертированы в 32-битные (signed long
int
и unsigned long int
):
signed short |
16-битное представление |
32-битное представление |
signed long |
unsigned long |
---|---|---|---|---|
-2 | 0xFFFE | 0xFFFFFFFE | -2 | 4294967294 |
32766 | 0x7FFE | 0x00007FFE | 32766 | 32766 |
Правило:
Если знаковое целое конвертируется в более длинное знаковое
целое, то значение не изменится.
Правило:
Если знаковое целое конвертируется в более короткий тип, то
результат зависит от реализации.
В Watcom C/16 знаковые целые конвертируются в более короткий тип с сохранением младшей (менее значащей) части большего типа.
Правило:
Если число с плавающей точкой преобразуется в целое, то
дробная часть отбрасывается. Если значение целой части не может быть
представлено целым типом, то результат — не определён.
Следовательно, конвертировать число с плавающей точкой в целое можно только в диапазоне того целого типа, в который это преобразование производится. См. раздел Целочисленные типы для уточнения диапазона целых.
Правило:
Если значение целого типа конвертируется в тип с плавающей
точкой, и целое значение не может быть точно представлено типом с плавающей
точкой, то значение будет округляться с недостатком или избытком.
Округление чисел с плавающей точкой зависит от реализации. Техника, которую
будет использовать компилятор можно узнать из макро FLT_ROUNDS
,
находящегося в заголовочном файле <float.h>
. Следующая таблица
поясняет смысл различных значений:
FLT_ROUND |
Округление |
---|---|
-1 | неопределённое |
0 | в сторону нуля |
1 | до ближайшего числа |
2 | в сторону плюс бесконечности |
3 | в сторону минус бесконечности |
Компиляторы Watcom C/16 и C/32 будут округлять до ближайшего числа.
(Значение FLT_ROUNDS
равно 1.)
Правило:
Если значение с плавающей точкой конвертируется в больший тип
с плавающей точкой (float
в double
,
float
в long double
, или double
в
long double
), то значение не изменится.
Правило:
Если тип с плавающей точкой преобразуется в тип с плавающей
точкой с меньшим диапазоном, то результат — не определён, если значение
выходит за границы меньшего типа. Если значение находится внутри диапазона,
но не может быть представлено точно, то будет производиться округление,
зависящее от реализации.
Компиляторы Watcom C/16 и C/32 будут округлять до ближайшего числа.
(Значение FLT_ROUNDS
равно 1.)
Всякий раз, когда два значения используются в бинарном операторе, в котором ожидаются арифметические типы (целые или с плавающей точкой), происходит неявное преобразование типа. Большинство бинарных операторов работают с двумя значениями одинаковых типов. Если два значения имеют разные типы, то тип с меньшим диапазоном всегда расширяется до типа с большим. Тип из таблицы, приведённой ниже, и тип находящийся под ним конвертируются в тип, находящийся выше в таблице.
long double |
double |
float |
unsigned long |
long |
unsigned int |
int |
Помните, что любой тип, меньший int
подвергается
целочисленному расширению, чтобы привести его к int
.
Следующая таблица демонстрирует типы результата при сложении различных типов:
Оператор | Тип результата |
---|---|
signed char + signed char | signed int |
unsigned char + signed int | signed int |
signed int + signed int | signed int |
signed int + unsigned int | unsigned int |
unsigned int + signed long | signed long |
signed int + unsigned long | unsigned long |
signed char + float | float |
signed long + double | double |
float + double | double |
float + long double | long double |
Когда производится вызов функции, компилятор C проверяет, определена ли уже функция, либо есть ли у неё прототип. Если это так, то аргументы функции преобразуются к указанным типам. Если нет, то аргументы преобразуются как показано ниже:
float
расширяются до double
.
Если определение функции не содержит параметров, совпадающих с расширенными типами, то поведение не определено.