Преобразования базовых типов данных, соответствующих целым числам, не всегда сохраняют значения переменных и могут приводить к трудно обнаруживаемым ошибкам. В ряде случаев необходимо апеллировать к внутренним формам представления и действиям над ними. Преобразование формы представления может включать в себя:
- преобразование целой переменной в переменную с плавающей точкой, и наоборот;
- увеличение или уменьшение разрядности машинного слова, то есть «растягивание» или «усечение» целой переменной;
- преобразование знаковой формы представления целого в беззнаковую, и наоборот.
Уменьшение разрядности машинного слова всегда происходит путем отсечения старших разрядов числа, что может привести к ошибкам потери значащих цифр и разрядов:
int n=0x7654;
char с; с = n; //Потеря значащих цифр (0x54)
Увеличение разрядности приводит к появлению дополнительных старших разрядов числа. При этом способ их заполнения зависит от формы представления целого и обеспечивает сохранение значения переменной в данной форме представления:
|
|
- для беззнаковых целых заполнение производится нулями;
- для целых со знаком дополнительные разряды заполняются одним и тем же значением знакового (старшего) разряда.
int n; unsigned u;
// Значение n=0xFF84
char с=0х84; n= с;
//Значение u=0x0084
unsigned char uc=0x84; u = uc;
При преобразовании вещественного к целому происходит потеря дробной части, при этом возможно возникновение ошибок переполнения и потери значащих цифр, когда полученное целое имеет слишком большое значение.
double d1 =855.666, d2=0.5E16;
int n;
n = d1; // Отбрасывание дробной части
n = d2; // Потеря значимости
Преобразование знаковой формы в беззнаковую и обратно не сопровождается изменением значения целого числа и вообще не приводит к выполнению каких-либо действий в программе. В таких случаях транслятор «запоминает», что форма представления целого изменилась, и только.
int n = -1;
unsigned d;
d = n; // Значение d=0xFFFF (-1)
Самое главное для программиста, что в языке не предусмотрены средства автоматической реакции на ошибки преобразования типов данных, поэтому «отлавливать» их должна сама программа.
Преобразования типов данных операндов происходят в программе в трех случаях:
• при выполнении операции присваивания, когда значение переменной или выражения из правой части запоминается в переменной в левой части;
• при прямом указании на необходимость изменения типа данных переменной или выражения для чего используется операция явного преобразования типа;
• при выполнении бинарных операций над операндами различных типов, когда более «длинный» операнд превалирует над более «коротким», вещественное - над целым, а беззнаковое - над знаковым.
|
|
В последнем случае неявные преобразования выполняются в такой последовательности: короткие типы данных (знаковые и беззнаковые) удлиняются до int и double, а выполнение любой бинарной операции с одним long double, double, long, unsigned ведет к преобразованию другого операнда в тот же тип. Это может сопровождаться перечисленными выше действиями: увеличением разрядности операнда путем его «удлинения», преобразованием в форму с плавающей точкой и изменением беззнаковой формы представления на знаковую, и наоборот.
Следует обратить внимание на одну тонкость: если в процессе преобразования требуется увеличение разрядности переменной, то на способ ее «удлинения» влияет только наличие или отсутствие знака у самой переменной. Второй операнд, к типу которого осуществляется приведение, на этот процесс не влияет:
long k=0x21;
unsigned short d=0xFF00;
//0x00000021 + 0xFF00 =
//=0x00000021 + 0x0000FF00 = 0x0000FF21
cout<<hex<<k + d<<’\n’;
В данном случае производится преобразование короткого целого без знака (unsigned) в длинное целое со знаком (long). В процессе преобразования «удлинение»переменной d производится как беззнаковое (разряды заполняются нулями), хотя второй операнд и имеет знак.
Рассмотрим еще несколько примеров.
int i; i=0xFFFF;
Целая переменная со знаком получает значение FFFF, что соответствует -1 для знаковой формы в дополнительном коде. Изменение формы представления с беззнаковой на знаковую не сопровождается никакими действиями.
short i = 0xFFFF;
int k; k=i;
Преобразование short в int сопровождается «удлинением» переменной, что c учетом представления i со знаком дает FFFFFFFF, то есть целое со значением -1.
unsigned short n =0xFF00;
int i;
i = n;
Переменная n «удлиняется» как целое без знака, то есть переменная i получит значение 0000FF00.
short i; unsigned short u;
i = u= 0xFFFF;
if (i > 5) … // "Ложь"
if (u > 5)... // "Истина"
Значения переменных без знака и со знаком равны FFFF или -1. Но результаты сравнения противоположны, так как во втором случае сравнение проводится для беззнаковых целых по их абсолютной величине, а в первом случае - путем проверки знака результата вычитания, то есть с учетом знаковой формы представления чисел.
Стандартные программные решения