Если в арифметических операциях участвуют значения разных типов, то компилятор неявно пытается привести их к одному типу. Кроме того, когда мы присваиваем переменной какое-либо значение, это значение всегда приводится к типу переменной. Например:
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
Например:
int a = 10;
#include <stdio.h>
int main(void)
{
int number1 = 10;
double number2 = 4;
double result = number1 + number2; // 14.000000
printf("result = %f \n", result); // result = 14.000000
return 0;
}
В выражении number1 + number2 число number2 представляет тип double, поэтому число number1
будет автоматически приводиться к числу double. И результат операции сложения также будет представлять тип double.
С помощью специальной операции преобразования мы можем явным образом привести данные к нужному типу. Например:
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.
Для выполнения операции приведении в скобках указывается тот тип, к которому надо привести значение:
int number = 70;
char symbol = (char) number;
printf("symbol = %c \n", symbol); // F
printf("symbol (int code) = %d \n", symbol); // 70
В ряде случаев преобразования сопровождаются потерей информации, например, когда числа большей разрядности (скажем размером 4 байт) получаем число меньшей разрядности (например, в 2 байта). Без потери информации проходят следующие цепочки преобразований:
char -> short -> int -> long
unsigned char -> unsigned short -> unsigned int -> unsigned long
float -> double -> long double
При всех остальных преобразованиях, которые не входят в эти цепочки, мы можем столкнуться с потерей точности данных. Так, в примере выше преобразование от int к char не является безопасным, поэтому к таким преобразованиям следует относиться с осторожностью. Например:
#include <stdio.h>
int main(void)
{
int number1 = 300;
char code = number1; // потеря точности - число number1 усекается до 1 байта
printf("code = %d \n", code); // code = 44
return 0;
short number2 = 100000; // потеря точности - число 100000 усекается до 2 байт
printf("number2 = %d \n", number2); // number2 = -31072
}
Здесь две ситуации небезопасных преобразований. В первом случае число типа int, которое равно 300, присваивается переменной типа char. В итоге переменная code будет равна 44. Почему? Число
300 в двоичной системе:
0000000100101100
Оставляем только первый младший байт:
00101100
И у нас получается число 44 в десятичной системе.
Во втором случае число 100000 (которое по умолчанию представляет тип int), усекается до разрядности типа short -
до двух байт.
short number = 100000;
В итоге число number в реальности будет равно -31072.