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


Если в арифметических операциях участвуют значения разных типов, то компилятор неявно пытается привести их к одному типу. Кроме того, когда мы присваиваем переменной какое-либо значение, это значение всегда приводится к типу переменной. Например:

1
2
char c = 6;
int d = c;
Переменной d, которая представляет тип int, присваивается значение типа char, поэтому компилятор выполняет приведение значения от типа char к типу int.

В то же время не всегда преобразования могут быть безопасными, поскольку разные типы имеют разное внутреннее представление. И просто так перейти от одного представления к другому без потери точности данных не всегда возможно.

Рассмотрим, какие преобразования применяет компилятор при арифметических операциях:

Если один из операндов имеет тип long double, то второй операнд тоже будет преобразован в тип long double

Если предыдущий пункт не выполняется и если один из операндов имеет тип double, то второй операнд тоже будет преобразован к типу double

Если предыдущий пункт не выполняется и если один из операндов имеет тип float, то второй операнд тоже будет преобразован к типу float

Если предыдущий пункт не выполняется и если один из операндов имеет тип unsigned long int, то второй операнд тоже будет преобразован к типу unsigned long int

Если предыдущий пункт не выполняется и если один из операндов имеет тип long, то второй операнд тоже будет преобразован к типу long

Если предыдущий пункт не выполняется и если один из операндов имеет тип unsigned, то второй операнд тоже будет преобразован к типу unsigned

Если предыдущий пункт не выполняется то оба операнда приводятся к типу int

Например:

1
2
3
int a = 10;
double b = 4;
double c = a + b; // 14.000000
В выражении a + b число b представляет тип double, поэтому число a будет автоматически приводиться к числу double. И результат операции сложения также будет представлять тип double.

Операция преобразования
С помощью специальной операции преобразования мы можем явным образом привести данные к нужному типу. Например:

1
2
3
4
5
6
7
8
int a = 10;
int b = 4;
int c = a / b; // 2
double d = a / b; // 2.00000
double e = (double)a / (double)b; // 2.50000
printf("c = %d \n", c);
printf("d = %f \n", d);
printf("e = %f \n", e);
В выражении int c = a / b; результат деления будет целочисленный - 2, при котором дробная часть будет отброшена, так как оба операнда операции представляют целые числа.

В выражении double d = a / b; результат деления будет представлять вещественное число - 2.00000, но так как оба операнда являются целыми числами, то опять же результат операции будет представлять целое число 2, и только поле выполнения деления произойдет присвоение результата переменной d с приведением значения 2 от типа int к типу double.

В выражении double e = (double)a / (double)b применяется явное преобразование данных к типу double, поэтому и результат деления будет представлять вещественное число - 2.50000.

Для выполнения операции приведении в скобках указывается тот тип, к которому надо привести значение:

1
2
3
4
int number = 70;
char symbol = (char) number;
printf("symbol = %c \n", symbol); // F
printf("symbol (int code) = %d \n", symbol); // 70
В ряде случаев преобразования сопровождаются потерей информации. Без потери информации проходят следующие цепочки преобразований:

char -> short -> int -> long

unsigned char -> unsigned short -> unsigned int -> unsigned long

float -> double -> long double

При всех остальных преобразованиях, которые не входят в эти цепочки, мы можем столкнуться с потерей точности данных. Так, в примере выше преобразование от int к char не является безопасным, поэтому к таким преобразованиям следует относиться с осторожностью.