Содержание

Преобразование типов

Всякий раз, когда в операторе используются два операнда, над одним из них или над обоими производится некоторый род преобразований. Например, 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 проверяет, определена ли уже функция, либо есть ли у неё прототип. Если это так, то аргументы функции преобразуются к указанным типам. Если нет, то аргументы преобразуются как показано ниже:

Если определение функции не содержит параметров, совпадающих с расширенными типами, то поведение не определено.