Преобразование типов
В этой статье есть несколько проблем. Пожалуйста, помогите улучшить его или обсудите эти проблемы на странице обсуждения . ( Узнайте, как и когда удалять эти шаблонные сообщения )
|
В информатике преобразование типов [1] [2] типовое литье , [1] [3] принуждение типа , [3] и жонглирование типами [4] [5] это разные способы изменения выражения из одного типа данных в другой. Примером может служить преобразование целочисленного значения в значение с плавающей запятой или его текстовое представление в виде строки и наоборот. Преобразования типов могут использовать определенные возможности иерархий типов или представлений данных . Два важных аспекта преобразования типов: происходит ли оно неявно (автоматически) или явно . [1] [6] и преобразуется ли базовое представление данных из одного представления в другое, или данное представление просто переинтерпретируется как представление другого типа данных. [6] [7] В общем, как примитивные , так и составные типы данных можно преобразовать .
Каждый язык программирования имеет свои правила преобразования типов. Языки со строгой типизацией обычно мало выполняют неявные преобразования и препятствуют повторной интерпретации представлений, тогда как языки со слабой типизацией выполняют множество неявных преобразований между типами данных. Язык слабой типизации часто позволяет компилятору произвольно интерпретировать элемент данных как имеющий разные представления — это может быть неочевидная ошибка программирования или технический метод, позволяющий напрямую работать с базовым оборудованием.
В большинстве языков слово приведение используется для обозначения неявного преобразования, выполняемого либо во время компиляции, либо во время выполнения . Например, в выражении, сочетающем целые числа и числа с плавающей запятой (например, 5 + 0,1), компилятор автоматически преобразует целочисленное представление в представление с плавающей запятой, чтобы дроби не были потеряны. Явные преобразования типов обозначаются либо написанием дополнительного кода (например, добавлением идентификаторов типов или вызовом встроенных подпрограмм ), либо путем написания подпрограмм преобразования, которые компилятор будет использовать, когда в противном случае он остановился бы из-за несоответствия типов.
В большинстве АЛГОЛ -подобных языков, таких как Pascal , Modula-2 , Ada и Delphi , преобразование и приведение — это совершенно разные понятия. В этих языках преобразование означает неявное или явное изменение значения из одного формата хранения данных в другой, например, 16-битное целое число в 32-битное целое число. Потребности в хранении могут измениться в результате преобразования, включая возможную потерю точности или усечение. слово cast С другой стороны, относится к явному изменению интерпретации битового шаблона, представляющего значение, с одного типа на другой. Например, 32 смежных бита можно рассматривать как массив из 32 логических значений, 4-байтовую строку, 32-битное целое число без знака или значение с плавающей запятой одинарной точности IEEE. Поскольку сохраненные биты никогда не изменяются, для осмысленного преобразования программист должен знать детали низкого уровня, такие как формат представления, порядок байтов и требования к выравниванию.
В семействе языков C и ALGOL 68 слово cast обычно относится к явному преобразованию типа (в отличие от неявного преобразования), вызывая некоторую двусмысленность относительно того, является ли это повторной интерпретацией битового шаблона или реальным представлением данных. конверсия. Более важным является множество способов и правил, применимых к тому, какой тип данных (или класс) находится по указателю и как указатель может быть скорректирован компилятором в таких случаях, как наследование объекта (класса).
Явное приведение типов на разных языках
[ редактировать ]Есть
[ редактировать ]Ada предоставляет общую библиотечную функцию Unchecked_Conversion. [8]
C-подобные языки
[ редактировать ]Неявное преобразование типов
[ редактировать ]Неявное преобразование типов, также известное как приведение или жонглирование типами , представляет собой автоматическое преобразование типов, выполняемое компилятором . Некоторые языки программирования позволяют компиляторам обеспечивать приведение; другие требуют этого.
В выражении смешанного типа данные одного или нескольких подтипов могут быть преобразованы в супертип по мере необходимости во время выполнения, чтобы программа работала правильно. Например, следующий код языка C является допустимым :
double d;
long l;
int i;
if (d > i) d = i;
if (i > l) l = i;
if (d == l) d *= 2;
Хотя д , земля Я принадлежу к разным типам данных, они будут автоматически конвертироваться в одинаковые типы данных каждый раз, когда выполняется сравнение или присваивание. Такое поведение следует использовать с осторожностью, так как могут возникнуть непредвиденные последствия . Данные могут быть потеряны при преобразовании представлений из чисел с плавающей запятой в целые числа, поскольку дробные компоненты значений с плавающей запятой будут усечены (округлены в сторону нуля). И наоборот, точность может быть потеряна при преобразовании представлений из целого числа в число с плавающей запятой, поскольку тип с плавающей запятой может быть неспособен точно представлять все возможные значения некоторого целочисленного типа. Например, float
может быть типом одинарной точности IEEE 754 , который не может точно представлять целое число 16777217, тогда как 32-битный целочисленный тип может. Это может привести к неинтуитивному поведению, о чем свидетельствует следующий код:
#include <stdio.h>
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
плавать, когда его сравнивают с f_value
. Преобразование приводит к потере точности, в результате чего значения перед сравнением становятся равными.
Важные выводы:
float
кint
вызывает усечение , т. е. удаление дробной части.double
кfloat
вызывает округление цифры.long
кint
вызывает отбрасывание лишних битов старшего порядка.
Тип продвижения
[ редактировать ]Одним из особых случаев неявного преобразования типов является повышение типа, при котором объект автоматически преобразуется в другой тип данных, представляющий надмножество исходного типа. Расширения обычно используются с типами, меньшими, чем собственный тип арифметико-логического устройства (АЛУ) целевой платформы, перед арифметическими и логическими операциями, чтобы сделать такие операции возможными или более эффективными, если АЛУ может работать более чем с одним типом. C и C++ выполняют такое повышение для объектов логического, символьного, широкого символьного, перечисления и коротких целочисленных типов, которые повышаются до int, а также для объектов типа float, которые повышаются до double. В отличие от некоторых других преобразований типов, повышения никогда не теряют точности и не изменяют значение, хранящееся в объекте.
На Яве :
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-подобным) способом, первый из которых называется приведением проверенного типа . [9]
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_cast<Bulldog*>(animal); // compiles only if either Animal or Bulldog is derived from the other (or same)
b = dynamic_cast<Bulldog*>(animal); // if (animal is Bulldog), b = (Bulldog*) animal, else b = nullptr
Bulldog& br = static_cast<Bulldog&>(*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
delete animal; // always free resources
animal = nullptr;
b = dynamic_cast<Bulldog*>(animal); // b == nullptr
Эйфелева
[ редактировать ]В Эйфеле понятие преобразования типов интегрировано в правила системы типов. Правило назначения гласит, что назначение, например:
x := y
допустимо тогда и только тогда, когда тип его исходного выражения, y
в данном случае совместимо с типом целевой сущности, x
в этом случае. В этом правиле совместимость с означает, что тип исходного выражения либо соответствует типу целевого выражения , либо преобразуется в него. Соответствие типов определяется знакомыми правилами полиморфизма в объектно-ориентированном программировании . Например, в приведенном выше задании тип y
соответствует типу x
если класс, на котором y
основано, является потомком того, на чем основано x
основано.
Определение преобразования типов в Eiffel
[ редактировать ]Действия преобразования типов в Eiffel, в частности преобразование в и преобразование из, определяются как:
Тип, основанный на классе CU, преобразуется в тип T, основанный на классе CT (а T преобразуется из U), если либо
- CT имеет процедуру преобразования, использующую U в качестве типа преобразования, или
- CU имеет запрос на преобразование, в котором в качестве типа преобразования указан T.
Пример
[ редактировать ]Eiffel — это полностью совместимый язык с Microsoft .NET Framework . До разработки .NET у Eiffel уже были обширные библиотеки классов. Использование библиотек типов .NET, особенно с часто используемыми типами, такими как строки, создает проблему преобразования. Существующее программное обеспечение Eiffel использует классы строк (например, STRING_8
) из библиотек Eiffel, но программное обеспечение Eiffel, написанное для .NET, должно использовать строковый класс .NET ( System.String
) во многих случаях, например, при вызове методов .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
.
Эйфелев класс 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
ключевое слово. [10]
let x = 1000;
println!("1000 as a u16 is: {}", x as u16);
Тип утверждения
[ редактировать ]Связанная концепция в системах статического типа называется утверждением типа , которое указывает компилятору обрабатывать выражение определенного типа, игнорируя его собственный вывод. Утверждение типа может быть безопасным (выполняется проверка во время выполнения) или небезопасным. Утверждение типа не преобразует значение из одного типа данных в другой.
Машинопись
[ редактировать ]В TypeScript утверждение типа выполняется с помощью as
ключевое слово: [11]
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
В приведенном выше примере document.getElementById
объявлено, что он возвращает HTMLElement
, но вы знаете, что он всегда возвращает HTMLCanvasElement
, который является подтипом HTMLElement
, в этом случае. Если это не так, последующий код, основанный на поведении HTMLCanvasElement
не будет работать корректно, поскольку в Typescript нет проверки утверждений типа во время выполнения.
В Typescript нет общего способа проверить, относится ли значение к определенному типу во время выполнения, поскольку нет поддержки типов во время выполнения. Однако можно написать пользовательскую функцию, которая будет сообщать пользователю компилятору, если значение имеет определенный тип «нет». Такая функция называется typeguard и объявляется с возвращаемым типом x is Type
, где x
является параметром или this
, вместо boolean
.
Это позволяет содержать утверждения небезопасного типа в функции проверки, а не разбрасывать их по базе кода.
Идти
[ редактировать ]В Go утверждение типа можно использовать для доступа к конкретному значению типа из значения интерфейса. Можно с уверенностью утверждать, что он будет паниковать (в случае одного возвращаемого значения) или возвращать нулевое значение (если используются два возвращаемых значения), если значение не относится к этому конкретному типу. [12]
t := i.(T)
Утверждения этого типа сообщают системе, что i
имеет тип T
. Если это не так, он паникует.
Неявное приведение с использованием нетегированных объединений
[ редактировать ]Многие языки программирования поддерживают типы объединения , которые могут содержать значения нескольких типов. Немаркированные объединения предусмотрены в некоторых языках со свободной проверкой типов, таких как C и PL/I , а также в исходном языке Pascal . Их можно использовать для интерпретации битовой комбинации одного типа как значения другого типа.
Проблемы безопасности
[ редактировать ]В хакерстве приведение типов — это неправильное использование преобразования типов для временного изменения переменной по сравнению с тем, как она была первоначально определена. типа данных [13] Это предоставляет хакерам возможности, поскольку при преобразовании типа после того, как переменная была «приведена к типу», чтобы стать другим типом данных, компилятор будет рассматривать эту взломанную переменную как новый тип данных для этой конкретной операции. [14]
См. также
[ редактировать ]- Даункастинг
- Информация о типе времени выполнения § C++ – динамическое приведение и приведение Java
- Тип каламбур
Ссылки
[ редактировать ]- ^ Перейти обратно: а б с Мехротра, Дирадж (2008). Информатика С. Чанда . С. Чанд. стр. 81–83. ISBN 978-8121929844 .
- ^ Языки программирования — Проектирование и конструкции . Публикации Лакшми. 2013. с. 35. ISBN 978-9381159415 .
- ^ Перейти обратно: а б Рейли, Эдвин (2004). Краткая энциклопедия информатики . Джон Уайли и сыновья. стр. 82, 110 . ISBN 0470090952 .
- ^ Фентон, Стив (2017). Pro TypeScript: разработка JavaScript в масштабе приложения . Апресс. стр. XXII. ISBN 978-1484232491 .
- ^ «PHP: Жонглирование типами — Руководство» . php.net . Проверено 27 января 2019 г.
- ^ Перейти обратно: а б Олссон, Микаэль (2013). Краткий справочник по синтаксису C++ . Апресс. стр. 87–89. ISBN 978-1430262770 .
- ^ Крузе, Рудольф; Боргельт, Кристиан; Брауне, Кристиан; Мостагим, Саназ; Штайнбрехер, Матиас (16 сентября 2016 г.). Вычислительный интеллект: методологическое введение . Спрингер. п. 269. ИСБН 978-1447172963 .
- ^ «Непроверенные преобразования типов» . Информационный центр Ады . Проверено 11 марта 2023 г.
- ^ Мессенбёк, Ханспетер (25 марта 2002 г.). «Продвинутый C#: проверенное приведение типов» (PDF) . Институт системного программного обеспечения, Университет Иоганна Кеплера, Линц, факультет компьютерных наук. п. 5 . Проверено 4 августа 2011 г. в учебнике по C#
- ^ «Литье — ржавчина на примере» . doc.rust-lang.org .
- ^ " "Машинописная документация" " .
- ^ « Экскурсия по Го » .
- ^ Jon Erickson Hacking, 2nd Edition: The Art of Exploitation 2008 1593271441 p51 «Приведение типов — это просто способ временно изменить тип данных переменной, несмотря на то, как он был первоначально определен. Когда переменная приводится к другому типу, компилятор по сути сказано обрабатывать эту переменную так, как если бы это был новый тип данных, но только для этой операции. Синтаксис приведения типов следующий: (typecast_data_type) переменная ..."
- ^ Арпита Гопал Лупа C 2009 8120338618 стр. 59 «Из вышеизложенного ясно, что использование приведения типов заключается в том, чтобы заставить переменную одного типа действовать как другой тип для одной операции. Таким образом, используя эту возможность приведения типов, можно создавать символы ASCII путем приведения целого числа к типу. его ..."
Внешние ссылки
[ редактировать ]- Кастинг в Аде
- Кастинг в C++
- Справочное руководство по C++ «Почему я ненавижу операторы приведения C++», Дэнни Калев
- Кастинг на Java
- Неявные преобразования в C#
- Неявное приведение типов на Cppreference.com
- Статические и реинтерпретационные приведения в C++
- Повышающее и понижающее приведение в F#