Программирование и основы алгоритмизации УТС / Лекции / 12. Продвижение типов
.pdfПродвижение типов
Продвижением типов называется автоматическое приведение типов операндов при вычислении значений бинарных операций в выражениях. Продвижение типов преобразует значения операндов к некоторому одному типу, так как большинство бинарных операций может быть выполнено только над двумя операндами одного типа, например int–int, double–double и т.д., а не int–double и т.п. Это, с одной стороны, упрощает реализацию компилятора (каждая операция реализуется только для пар одноименных типов данных, а не для всех возможных пар) и, с другой, избавляет программиста от необходимости выполнять приведение типов операндов самостоятельно. Во многих случаях тип результата операции также зависит от типов операндов.
В спецификации языка C и во многих справочных и учебных пособиях описание продвижения типов представлено в виде свода правил «если… то…», однако этот механизм можно представить следующей диаграммой:
char |
unsigned char |
short |
unsigned short |
int |
unsigned int |
long unsigned long
float
double
long double
Преобразование типа операнда выполняется обязательно и независимо от типа другого операнда.
Преобразование типа операнда выполняется только в том случае, если другой операнд беззнаковый.
Преобразование типа операнда выполняется во всех остальных случаях, если другой операнд имеет другой тип.
Из диаграммы видно, что продвижение типов выполняется всегда от меньших типов к большим, имеющим не меньшие диапазоны значений, точности и порядка, чтобы значение операнда не было искажено (исключение – преобразование целочисленного отрицательного значения в беззнаковое). При этом преобразование значений и их интерпретация выполняются по тем же правилам, что и приведение типов. В приведенной ниже таблице представлены примеры продвижения типов, наглядно демонстрирующие, что результирующий тип выбирается при движении по диаграмме в направлении стрелок, до типа, ближайшего к исходным:
Исходные типы операндов |
Результирующий тип |
|
char |
int |
int |
int |
double |
double |
int |
unsigned int |
unsigned int |
float |
int |
double |
unsigned int |
long |
unsigned long |
char |
unsigned char |
unsigned int |
unsigned short |
long double |
long double |
Следует учитывать, что это общая схема продвижения типов, но к бинарным операциям разных групп (по назначению) она применима в различной степени:
–операнды арифметических операций приводятся к одному типу и результат будет иметь тот же тип;
–операнды логических поразрядных операций (кроме двоичного сдвига) приводятся к одному типу и результат будет иметь тот же тип;
–операнды операций двоичного сдвига не приводятся к одному типу, а результат имеет тот же тип, что и левый операнд;
–операнды операций отношения приводятся к одному типу, но результат всегда имеет тип int (логическое значение);
–операнды логических операций не приводятся к одному типу и рассматриваются как логические независимо друг от друга, а результат всегда имеет тип int (логическое значение);
–правый операнд операции простого присваивания приводится к типу левого операнда и результат будет иметь тот же тип;
–операнды операций сложного присваивания приводятся к одному типу и выполняется арифметическая или поразрядная операция, результат которой приводится к типу левого операнда и результат будет иметь тот же тип;
–операнды операции «запятая» не приводятся к одному типу, а тип результата будет соответствовать типу правого операнда.
Так как в выражениях при выполнении операций могут сочетаться продвижение
типов, явное и неявное их приведение, то результат вычислений может оказаться несколько неожиданным, например:
int a = –3; unsigned int b = 1; long c;
c = a + b;
С математической точки зрения: –3 + 1 = –2. Однако в приведенном примере переменная c примет значение 65534:
1.при определении переменной b значение 1 типа int будет приведено к беззнаковому и не изменится;
2.при вычислении a+b в результате продвижения типов значения операндов будут
приведены к типу unsigned int, при этом левый операнд станет равен
–310 = 1111'1111'1111'11012 → 1111'1111'1111'11012 = 6553310;
3.в результате суммирования будет получен результат 65534 типа unsigned int;
4.при выполнении присваивания, полученный результат будет приведен к типу long, причем при его расширении, как беззнакового, старшие разряды будут заполнены нулями и число интерпретируется как положительное.
Вподобных случаях, зная механизм продвижения типов и правила преобразования значений, проблему можно легко решить либо явным приведением типов (например,
используя (long)a или (int)b в последнем выражении), либо выбором других типов данных при определении переменных.