Явное и неявное приведение типов

Явное и неявное приведение типов

Приведе́ние (преобразование) ти́па (англ. type conversion , typecasting , coercion ) — в информатике преобразование значения одного типа в значение другого типа.

Содержание

Описание [ править | править код ]

Выделяют приведения типов:

Явное приведение задаётся программистом в тексте программы с помощью:

  • конструкции языка;
  • функции, принимающей значение одного типа и возвращающей значение другого типа.

Неявное приведение выполняется транслятором (компилятором или интерпретатором) по правилам, описанным в стандарте языка. Стандарты большинства языков запрещают неявные преобразования.

В слабо типизированных объектно-ориентированных языках, таких как C++, механизм наследования реализуется посредством приведения типа указателя на текущий объект к базовому классу (в типобезопасных, таких как OCaml, понятие о приведении типов отсутствует принципиально, и допустимость обращения к компоненту подтипа контролируется механизмом проверки согласования типов на этапе компиляции, а в машинном коде остаётся прямое обращение).

Неявное приведение типа [ править | править код ]

Неявное приведение типа в языках C/C++ [ править | править код ]

Неявное приведение типов происходит в следующих случаях [1] :

  • после вычисления операндов бинарных арифметических, логических, битовых операций, операций сравнения, а также 2-го или 3-го операнда операции «?:»; значения операндов приводятся к одинаковому типу;
  • перед выполнением присваивания;
  • перед передачей аргумента функции;
  • перед возвратом функцией возвращаемого значения;
  • после вычисления выражения конструкции switch значение приводится к целочисленному типу;
  • после вычисления выражений конструкций if , for , while , do — while значение приводится к типу bool .

Например, при выполнении бинарной арифметической операции значения операндов приводятся к одному типу. При наследовании указатели производного класса приводятся к указателям базового класса.

Рассмотрим пример на языке C.

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

При неявных преобразованиях возможны побочные эффекты. Например, при приведении числа вещественного типа к целому типу дробная часть отсекается (округление не выполняется). При обратном преобразовании возможно понижение точности из-за различий в представлении вещественных и целочисленных чисел. Например, в переменной типа float (число с плавающей точкой одинарной точности по стандарту IEEE 754), нельзя сохранить число 16 777 217 без потери точности, а в 32-битной переменной целого типа int — можно. Из-за потери точности операции сравнения одного и того же числа, представленного целым и вещественным типами (например, int и float ), могут давать ложные результаты (числа могут быть не равны).

Приведённый код выведет следующее, если размер int — 32 бита и компилятор поддерживает стандарт IEEE 754:

Явное приведение типа [ править | править код ]

Приведения типов в языке C [ править | править код ]

Для явного приведения типов имя типа указывается в круглых скобках перед переменной или выражением. Рассмотрим пример.

Для вычисления последнего выражения компилятор выполняет примерно следующие действия:

  • сначала переменная C символьного типа char явно приводится к целочисленному типу int путём расширения разрядности;
  • выполняется вычисление операндов для операции умножения. Левый операнд имеет тип int . Правый операнд — константа 10 , а такие константы по умолчанию имеют тип int . Так как оба операнда оператора «*» имеют тип int , неявное приведение типов не выполняется. Результат умножения тоже имеет тип int ;
  • выполняется вычисление операндов операции сложения. Левый операнд — результат умножения имеет тип int . Правый операнд — переменная Y имеет тип int . Так как оба операнда оператора «+» имеют тип int , неявное приведение к общему типу не выполняется. Результат сложения тоже имеет тип int ;
  • выполнение присваивания. Левый операнд — переменная X имеет тип int . Правый операнд — результат вычисления выражения, записанного справа от знака «=», тоже имеет тип int . Так как оба операнда оператора «=» имеют одинаковый тип, неявное приведение типов не выполняется.
Читайте также:  Приложение билайн для смарт тв самсунг

Но даже при этом возможны ошибки. Тип char может быть как знаковым ( signed char ), так и беззнаковым ( unsigned char ); результат зависит от реализации компилятора и такое поведение разрешено стандартом. Значение беззнакового типа char при преобразовании к знаковому типу int может оказаться отрицательным из-за особенностей реализации машинных инструкций на некоторых процессорах. Чтобы избежать неоднозначностей, рекомендуется явно указывать знаковость для типа char .

Приведения типов в языке C++ [ править | править код ]

В языке C++ существует пять операций для явного приведения типа. Первая операция — круглые скобки ( ( type_to ) expression_from ) поддерживается для сохранения совместимости с C. Остальные четыре операции записываются в виде

Громоздкие ключевые слова являются напоминанием программисту о том, что приведение типа чревато проблемами.

Операция static_cast [ править | править код ]

Назначение: допустимые приведения типов.

Операция static_cast аналогична операции «круглые скобки» с одним исключением: она не выполняет приведение указателей на неродственные типы (для этого применяется операция reinterpret_cast ).

  • преобразование между числовыми и enum, в том числе если неявное преобразование невозможно ( int → enum class ) или приводит к предупреждению «Возможная потеря точности» ( double → float );
  • приведение указателей к типу void * и наоборот;
  • приведение указателей на производные типы к указателям на базовые типы и наоборот;
  • выбор одной из нескольких перегруженных функций;
  • явный вызов конструктора с одним аргументом или перегруженной операции приведения типа;
  • приведение типа в шаблонах (компилятор уже при специализации шаблона решает, какие операции использовать);
  • приведение операндов тернарной условной операции « ?: » к одному типу (значения 2-го и 3-го операндов должны иметь одинаковый тип);

Ограничения на expression_from : нет.

Ограничения на type_to : должен существовать способ преобразования значения выражения expression_from к типу type_to , с помощью operator type_to или конструктора.

Производит ли операция static_cast код: в общем случае да (например, вызов перегруженной операции приведения типа или конструктора).

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

Операция dynamic_cast [ править | править код ]

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

Операция получает информацию о типе объекта expression_from с помощью RTTI. Если тип будет type_to или его подтипом, приведение выполняется. Иначе:

  • для указателей возвращается NULL;
  • для ссылок создаётся исключение std :: bad_cast .

Ограничения на expression_from : выражение должно быть ссылкой или указателем на объект, имеющий хотя бы одну виртуальную функцию.

Ограничения на type_to : ссылка или указатель на дочерний по отношению к expression_from тип.

Производит ли операция dynamic_cast код: да.

Логические ошибки возможны, если операции передать аргумент, не имеющий тип type_to , и не проверить указатель на равенство NULL (соответственно не обработать исключение std :: bad_cast ).

Операция const_cast [ править | править код ]

Назначение: снятие/установка модификатора(ов) const , volatile и/или mutable . Часто это применяется, чтобы обойти неудачную архитектуру программы или библиотеки, для стыковки Си с Си++, для передачи информации через обобщённые указатели void* .

Ограничения на expression_from : выражение должно возвращать ссылку или указатель.

Ограничения на type_to : тип type_to должен совпадать с типом выражения expression_from с точностью до модификатора(ов) const , volatile и mutable .

Производит ли операция const_cast код: нет.

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

При загрузке библиотеки в память процесса создаёт новый сегмент данных, в котором размещаются глобальные переменные. Код функции SomeDllFunction () находится в библиотеке и при вызове возвращает указатель на скрытый член глобального объекта класса string . Операция const_cast используется для удаления модификатора const .

Читайте также:  Знакомства в нижнем новгороде майл ру

Операция reinterpret_cast [ править | править код ]

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

Объект, возвращаемый выражением expression_from , рассматривается как объект типа type_to .

Ограничения на expression_from : выражение должно возвращать значение порядкового типа (любой из целых, логический bool или перечислимый enum ), указатель или ссылку.

Ограничения на type_to :

  • Если expression_from возвращает значение порядкового типа или указатель, тип type_to может быть порядковым типом или указателем.
  • Если expression_from возвращает ссылку, тип type_to должен быть ссылкой.

Производит ли операция reinterpret_cast код: нет.

Источники логических ошибок. Объект, возвращаемый выражением expression_from , может не иметь типа type_to . Нет никакой возможности проверить это, всю ответственность за корректность преобразования программист берёт на себя.

Обновл. 30 Дек 2019 |

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

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

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

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

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

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

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

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

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

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

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

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

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

Явное преобразование типов, когда разработчик использует один из операторов casts для выполнения конвертации объекта из одного типа данных в другой.

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

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

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

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

Когда значение из одного типа данных конвертируется в другой тип данных побольше (по размеру и по диапазону значений), то это называется числовым расширением. Например, 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 (они наиболее эффективны для выполнения разных операций).

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

Читайте также:  Apple id заблокирован в целях безопасности

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

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

Всем доброго времени суток, на связи Алексей Гулынин. В прошлой статье мы узнали, что такое .NET Framework. В данной статье я бы хотел поговорить про явное и неявное преобразование в C#. C# является строго типизированным языком. Тип переменной нужно указывать при её инициализации. Иногда возникает необходимость преобразовать переменную в другой тип. В C# существует явное и неявное преобразование типов ( implicitly convert ). Неявное преобразование характерно, в основном, для числовых типов: byte, short, int, long, float, double . Как это понимать? Тип данных byte неявно преобразовывается в типы данных short, int ,long . Тип данных short неявно преобразовывается в типы данных int, long . Int неявно преобразовывается в long, float неявно преобразовывается в double . А в double вообще все эти типы неявно преобразовываются.

Давайте на примере разберем неявное преобразование типов:

Почему такое преобразование возможно. Ответ кроется в том, что диапазон значений long перекрывает int (т.е. в long можно записать значение любого int ), диапазон значений short перекрывает byte и т.д.

Здесь мы видим, что преобразование прошло и сообщения выводятся. Однако обратное преобразование не работает, т.е. long неявно не преобразовывается в int , short в byte и т.д. При попытке выполнить такое преобразование компилятор поругается:

Компилятор говорит, "что неявное преобразование short в byte невозможно. Явное преобразование существует (вы пропустили cast?)". На этапе компиляции компилятор не знает, какое значение будет храниться в переменных b (типа byte) и s (типа short) , а не всякое число типа short можно положить в byte . Можно написать такое значение short , которое не влезет в byte .

Поэтому и выходит ошибка на этапе компиляции.

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

Существует ещё один способ преобразования данных с помощью класса Convert , в котором есть много статических методов (с префиксом To ). Данные статические методы выполняют преобразования. Переделаем предыдущий пример:

Здесь я добавил преобразование long в int . Тут есть несколько статических методов: ToInt16(), ToInt32(), ToInt64() . Разберем, что значат эти цифры на примере метода ToInt32 . Тип данных int занимает 4 байта (нам и нужно для хранения этой переменной 4 байта), а это 32 бита (4 байта * 8(в одном байте 8 битов) = 32 бита).

Также отмечу, что существует универсальный метод ToString() , который любой тип данных переводит в строку. А что если мы хотим строку перевести в число? Это требуется тогда, когда пользователь что-то вводит. Здесь поможет метод Parse . Рассмотрим на примере:

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

В качестве домашнего задание реализуйте такой алгоритм: выводить сообщение пользователю «Введите целое число» до тех пор пока он не введёт целое число. Другими словами, если он ввёл не целое число, то программа не должна завершаться и сообщение должно снова появляться.

В данной статье вы узнали, что такое конструкторы в C#.

На связи был Алексей Гулынин, оставляйте свои комментарии, увидимся в следующих статьях.

Ссылка на основную публикацию
Adblock detector