В информатике, преобразование типов, отливки типа, типа принуждения и типа жонглирования различные способы изменения в выражение из одного типа данных в другой. Примером может служить преобразование целочисленного значения в значение с плавающей запятой или его текстовое представление в виде строки и наоборот. Преобразования типов могут использовать преимущества определенных функций иерархий типов или представлений данных. Двумя важными аспектами преобразования типа являются то, происходит ли оно неявно (автоматически) или явно, и преобразуется ли базовое представление данных из одного представления в другое, или данное представление просто переинтерпретируется как представление другого типа данных. Как правило, можно преобразовывать как примитивные, так и составные типы данных.
У каждого языка программирования есть свои правила преобразования типов. Языки со строгой типизацией обычно выполняют мало неявных преобразований и препятствуют повторной интерпретации представлений, в то время как языки со слабой типизацией выполняют множество неявных преобразований между типами данных. Слабый язык типизации часто позволяет заставить компилятор произвольно интерпретировать элемент данных как имеющий разные представления - это может быть неочевидная ошибка программирования или технический метод для непосредственной работы с базовым оборудованием.
В большинстве языков слово принуждение используется для обозначения неявного преобразования либо во время компиляции, либо во время выполнения. Например, в выражении, сочетающем целые числа и числа с плавающей запятой (например, 5 + 0,1), компилятор автоматически преобразует целочисленное представление в представление с плавающей запятой, чтобы дроби не терялись. Явные преобразования типов указываются либо путем написания дополнительного кода (например, добавления идентификаторов типа или вызова встроенных подпрограмм ), либо путем кодирования подпрограмм преобразования, которые компилятор может использовать, когда в противном случае он остановился бы из-за несоответствия типов.
В большинстве Algol -подобных языков, таких как Pascal, Modula-2, Ada и Delphi, преобразования и литья отчетливо различные концепции. В этих языках преобразование относится либо к неявному, либо к явному изменению значения из одного формата хранения данных в другой, например, из 16-битного целого числа в 32-битное целое число. Потребности в хранении могут измениться в результате преобразования, включая возможную потерю точности или усечение. Слово литой, с другой стороны, относится к явным образом изменяя интерпретацию в битовой комбинации, представляющей собой значение от одного типа к другому. Например, 32 смежных бита можно рассматривать как массив из 32 логических значений, 4-байтовую строку, 32-битное целое число без знака или значение с плавающей запятой одинарной точности IEEE. Поскольку хранимые биты никогда не изменяются, программист должен знать подробности низкого уровня, такие как формат представления, порядок байтов и потребности выравнивания, чтобы их можно было осмысленно преобразовать.
В C семействе языков и АЛГОЛА 68, слово литой, как правило, относится к явным преобразованию типа (в отличие от неявного преобразования), в результате чего некоторые двусмысленностей об этом, является ли реинтерпретация битового шаблона или представление реальных данных конверсия. Более важным является множество способов и правил, которые применяются к тому, какой тип данных (или класс) находится с помощью указателя, и как указатель может быть скорректирован компилятором в таких случаях, как наследование объекта (класса).
Неявные преобразования типов, также известные как принуждение, является автоматическим преобразованием типа по компилятору. Некоторые языки программирования позволяют компиляторам обеспечивать принуждение; другие требуют этого.
В выражении смешанного типа данные одного или нескольких подтипов могут быть преобразованы в супертип по мере необходимости во время выполнения, чтобы программа работала правильно. Например, ниже указан допустимый код языка C :
double d; long l; int i; if (d gt; i) d = i; if (i gt; l) l = i; if (d == l) d *= 2;
Хотя d, l и i принадлежат к разным типам данных, они будут автоматически преобразовываться в одинаковые типы данных каждый раз, когда выполняется сравнение или присваивание. Это поведение следует использовать с осторожностью, поскольку могут возникнуть непредвиденные последствия. Данные могут быть потеряны при преобразовании представлений с плавающей запятой в целое число, поскольку дробные компоненты значений с плавающей запятой будут усечены (округлены в сторону нуля). И наоборот, точность может быть потеряна при преобразовании представлений из целых чисел в числа с плавающей запятой, поскольку тип с плавающей запятой может быть неспособен точно представить целочисленный тип. Например, это float
может быть тип одинарной точности IEEE 754, который не может точно представлять целое число 16777217, в то время как 32-битный целочисленный тип может. Это может привести к неинтуитивному поведению, о чем свидетельствует следующий код:
#include lt;stdio.hgt; int main(void) { int i_value = 16777217; float f_value = 16777216.0; printf("The integer is: %d\n", i_value); printf("The float is: %f\n", f_value); printf("Their equality: %d\n", i_value == f_value); }
В компиляторах, которые реализуют числа с плавающей запятой как одинарную точность IEEE, а целые числа - как минимум 32 бита, этот код выдаст такую своеобразную распечатку:
The integer is: 16777217 The float is: 16777216.000000 Their equality: 1
Обратите внимание, что 1 представляет равенство в последней строке выше. Это странное поведение вызвано неявным преобразованием i_value
в float при сравнении с f_value
. Преобразование приводит к потере точности, что приводит к равенству значений перед сравнением.
Важные выводы:
float
для int
причин усечения, то есть удаление дробной части.double
до float
причин округления цифр.long
to int
вызывает отбрасывание лишних битов более высокого порядка.Одним из особых случаев неявного преобразования типа является повышение типа, когда компилятор автоматически расширяет двоичное представление объектов целочисленных типов или типов с плавающей запятой. Повышения обычно используются с типами, меньшими, чем собственный тип арифметико-логического блока (ALU) целевой платформы, перед арифметическими и логическими операциями, чтобы сделать такие операции возможными, или более эффективными, если ALU может работать с более чем одним типом. C и C ++ выполняют такое продвижение для объектов типа boolean, character, wide character, enumeration и short integer, которые повышаются до int, а также для объектов типа float, которые повышаются до double. В отличие от некоторых других преобразований типов, рекламные акции никогда не теряют точности и не изменяют значение, хранящееся в объекте.
В Java :
int x = 3; double y = 3.5; System.out.println(x + y); // The output will be 6.5
Явное преобразование типа - это преобразование типа, которое явно определяется в программе (вместо того, чтобы выполняться компилятором для неявного преобразования типа). Он определяется пользователем в программе.
double da = 3.3; double db = 3.3; double dc = 3.4; int result = (int)da + (int)db + (int)dc; // result == 9 // if implicit conversion would be used (as with "result = da + db + dc"), result would be equal to 10
Есть несколько видов явного преобразования.
В объектно-ориентированных языках программирования объекты также могут быть понижены : ссылка базового класса приводится к одному из его производных классов.
В C # преобразование типа может быть выполнено безопасным или небезопасным (т. Е. C-подобным) способом, первый из которых называется приведением типа с проверкой.
Animal animal = new Cat(); Bulldog b = (Bulldog) animal; // if (animal is Bulldog), stat.type(animal) is Bulldog, else an exception b = animal as Bulldog; // if (animal is Bulldog), b = (Bulldog) animal, else b = null animal = null; b = animal as Bulldog; // b == null
В C ++ аналогичного эффекта можно добиться, используя синтаксис приведения в стиле C ++.
Animal* animal = new Cat; Bulldog* b = static_castlt;Bulldog*gt;(animal); // compiles only if either Animal or Bulldog is derived from the other (or same) b = dynamic_castlt;Bulldog*gt;(animal); // if (animal is Bulldog), b = (Bulldog*) animal, else b = nullptr Bulldogamp; br = static_castlt;Bulldogamp;gt;(*animal); // same as above, but an exception will be thrown if a nullptr was to be returned // this is not seen in code where exception handling is avoided animal = nullptr; b = dynamic_castlt;Bulldog*gt;(animal); // b == nullptr delete animal; // always free resources
В Eiffel понятие преобразования типов интегрировано в правила системы типов. Правило присвоения гласит, что такое задание, как:
x:= y
допустимо тогда и только тогда, когда тип его исходного выражения y
в этом случае совместим с типом его целевой сущности x
в этом случае. В этом правиле, совместимом с помощью, что тип выражения источника либо соответствует или обращенным к что мишени. Соответствие типов определяется знакомыми правилами полиморфизма в объектно-ориентированном программировании. Например, в приведенном выше назначении тип y
соответствует типу, x
если класс, на котором y
основан, является потомком того, на котором x
основан.
Действия преобразования типа в Eiffel, в частности преобразование в и преобразование из, определены как:
Тип, основанный на классе CU, преобразуется в тип T на основе класса CT (и T преобразуется из U), если либо
Eiffel - это полностью совместимый язык для Microsoft .NET Framework. До разработки.NET у Eiffel уже были обширные библиотеки классов. Использование библиотек типов.NET, особенно с часто используемыми типами, такими как строки, создает проблему преобразования. Существующее программное обеспечение Eiffel использует строковые классы (например, STRING_8
) из библиотек Eiffel, но программное обеспечение Eiffel, написанное для.NET, System.String
во многих случаях должно использовать строковый класс.NET (), например, при вызове методов.NET, которые ожидают элементы.NET. тип, передаваемый в качестве аргументов. Таким образом, преобразование этих типов туда и обратно должно быть как можно более плавным.
my_string: STRING_8 -- Native Eiffel string my_system_string: SYSTEM_STRING -- Native.NET string... my_string:= my_system_string
В приведенном выше коде объявлены две строки, по одной каждого типа ( SYSTEM_STRING
это совместимый с Eiffel псевдоним для System.String). Поскольку System.String
не соответствует STRING_8
, то указанное выше присвоение допустимо, только если System.String
преобразовано в STRING_8
.
Класс Eiffel STRING_8
имеет процедуру преобразования make_from_cil
для объектов типа System.String
. Процедуры преобразования также всегда обозначаются как процедуры создания (аналогично конструкторам). Ниже приводится отрывок из STRING_8
занятия:
class STRING_8... create make_from_cil... convert make_from_cil ({SYSTEM_STRING})...
Наличие процедуры преобразования делает присвоение:
my_string:= my_system_string
семантически эквивалентен:
create my_string.make_from_cil (my_system_string)
в котором my_string
создается как новый объект типа STRING_8
с содержимым, эквивалентным содержимому my_system_string
.
Чтобы обработать назначение с перевернутыми исходным источником и целью:
my_system_string:= my_string
класс STRING_8
также содержит запрос преобразования, to_cil
который будет генерировать System.String
из экземпляра STRING_8
.
class STRING_8... create make_from_cil... convert make_from_cil ({SYSTEM_STRING}) to_cil: {SYSTEM_STRING}...
Назначение:
my_system_string:= my_string
тогда становится эквивалентным:
my_system_string:= my_string.to_cil
В Eiffel настройка преобразования типов включается в код класса, но затем кажется, что это происходит автоматически, как явное преобразование типов в клиентском коде. Включает не только присвоения, но и другие типы вложений, такие как подстановка аргумента (параметра).
Rust не обеспечивает неявного преобразования (принуждения) типов между примитивными типами. Но явное преобразование типа (приведение) можно выполнить с помощью as
ключевого слова.
println!("1000 as a u16 is: {}", 1000 as u16);
При взломе приведение типов - это неправильное использование преобразования типа для временного изменения типа данных переменной по сравнению с тем, как он был изначально определен. Это предоставляет возможности для хакеров, поскольку при преобразовании типа после «приведения типа» переменной в другой тип данных компилятор будет рассматривать эту взломанную переменную как новый тип данных для этой конкретной операции.