Урок 55. Неявное преобразование типов данных

   ⁄ 

 Обновлено 25 Апр 2017

  ⁄   

С предыдущих уроков мы уже знаем, что значение переменной хранится в виде последовательности битов, а тип переменной указывает компилятору, как интерпретировать эти биты в нормальные значения. Разные типы данных могут представлять одно значение по-разному — например, int 4 и float 4.0 хранятся как совершенно разные двоичные шаблоны.

И как Вы думаете, что произойдет, если сделать следующее?

В таком случае компилятор не сможет просто скопировать биты со значения 4 типа int в переменную f типа float. Вместо этого ему нужно будет преобразовать целое число 4 в число типа с плавающей запятой, которое затем можно будет присвоить переменной f.

Процесс преобразования значения из одного типа данных в другой называется преобразованием типа. Они могут выполняться во многих случаях:

Присваивание или инициализация переменной значением другого типа данных:

Передача значения в функцию, где параметр функции имеет другой тип данных:

Возвращение значения из функции, где тип возвращаемого значения другой:

Использование бинарного оператора с операндами разных типов:

Во всех этих случаях (и во многих других) C++ будет использовать конвертацию типов.

Существует два основных способа преобразования: неявное преобразование типов, когда компилятор автоматически конвертирует один фундаментальный тип данных в другой и явное преобразование типов, когда разработчик использует оператор cast для управления конвертацией.

Мы рассмотрим неявное преобразование типов в этом уроке и явное в следующем.

Неявное преобразование типов

Неявное преобразование типа (также называемое автоматическим преобразованием типа или принудительным) выполняется всякий раз, когда требуется один фундаментальный тип данных, но предоставляется другой, и пользователь не указывает компилятору, как выполнить конвертацию (через оператор cast).

Все примеры выше относятся к случаям, когда будет выполняться автоматическая конвертация.

Есть два основных типа неявного преобразования: расширение и конверсия.

Числовое расширение

Всякий раз, когда значение одного типа данных преобразовывается в значение другого похожего типа данных, но более крупного, то это называется числовым расширением (или продвижением). Например, int может быть расширен в long, или float может быть расширен в double:

В C++ есть два типа расширений:

 Интегральное расширение (целочисленное). Включает в себя преобразование целочисленных типов, меньших, чем int (bool, char, unsigned char, signed char, unsigned short, signed short) в int (если это возможно) или unsigned int.

 Расширение типа с плавающей точкой. Конвертация из float в double.

Интегральное расширение и расширение типа с плавающей точкой используются в конкретных случаях, для преобразования меньших типов данных в типы int/unsigned int или double (они наиболее эффективные для выполнения разных операций).

Важно! Расширения всегда безопасны и не приводят к потере данных.

Числовые конверсии

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

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

Есть много правил числовой конверсии, но здесь мы рассмотрим только основные.

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

В этом примере мы присвоили огромное целочисленное значение типа int типу char (диапазон которого составляет от -128 до 127). Это приведет к переполнению и результатом будет:

48

Однако если число подходит по диапазону, то конвертация пройдет успешно. Например:

Здесь мы получим ожидаемый результат:

3
0.1234

В случаях со значениями типа с плавающей точкой могут произойти округления из-за худшей точности в меньших типах. Например:

В этом случае мы наблюдаем потерю в точности, так как точность в float меньше чем в double:

0.123456791

Конвертация из типа int в тип float успешна до тех пор, пока значения подходят по диапазону. Например:

Результат:

10

Конвертация из float в int успешна до тех пор, пока значения подходят по диапазону также. Но следует помнить, любая дробь отбрасывается. Например:

Дробная часть значения (.6) теряется и результат:

4

Обработка арифметических выражений

При обработке выражений компилятор разбивает каждое выражение на отдельные подвыражения. Арифметические операторы требуют, чтобы их операнды были одного типа. Чтобы это гарантировать, компилятор использует следующие правила:

  Если операнд — целое число типа меньше int, то оно подвергается интегральному расширению в int или в unsigned int.

  Если операнды все еще не подходят, тогда компилятор вычисляет операнд с наивысшим приоритетом и неявно преобразовывает тип другого операнда в соответствие к типу первого.

Приоритет типов операндов следующий:

  long double (самый высокий);

  double;

  float;

  unsigned long long;

  long long;

  unsigned long;

  long;

  unsigned int;

  int (самый низкий).

Мы можем использовать оператор typeid() (который находится в заголовочном файле typeinfo), чтобы узнать решающий тип данных в выражении.

В следующем примере в нас есть две переменные типа short:

Поскольку переменные short — целые числа и меньше типа int, то они подвергаются интегральному расширению в int. Результатом добавления двух int будет int:

int 9

Рассмотрим другой случай:

Здесь short подвергается интегральному расширению в int. Однако int и double по-прежнему не совпадают. Поскольку double находится выше в иерархии типов, целое число 2 преобразовывается в double 2.0, и два double равно double.

double 5.0

С этой иерархией иногда могут возникать интересные ситуации. Например:

Ожидается, что результатом выражения 5u − 10 будет -5, поскольку 5 − 10 = -5. Но на самом деле:

4294967291

Здесь значение signed int (10) подвергается расширению в unsigned int (которое имеет более высокий приоритет), и выражение вычисляется как unsigned int. А поскольку unsigned – это только положительные числа, то происходит переполнение, и мы получаем какую-то дичь.

Это одна из тех многих веских причин избегать использования unsigned int вообще.

Предупреждение: Microsoft Visual C++ 2005 не выдает предупреждений о небезопасных конверсиях значений типов signed/unsigned.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (6 оценок, среднее: 4,83 из 5)
Загрузка...
Поделиться в:
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *