stringtranslate.com

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

В информатике преобразование типов , [1] [2] приведение типов , [1] [3] приведение типов , [3] и жонглирование типами [4] [5] — это различные способы изменения выражения из одного типа данных в другой. Примером может служить преобразование целочисленного значения в значение с плавающей точкой или его текстовое представление в виде строки и наоборот. Преобразования типов могут использовать определенные особенности иерархий типов или представлений данных . Два важных аспекта преобразования типов — это то, происходит ли это неявно (автоматически) или явно , [1] [6] и преобразуется ли базовое представление данных из одного представления в другое, или данное представление просто переинтерпретируется как представление другого типа данных. [6] [7] В общем, как примитивные , так и составные типы данных могут быть преобразованы.

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

В большинстве языков слово coercion используется для обозначения неявного преобразования, либо во время компиляции, либо во время выполнения . Например, в выражении, смешивающем целые числа и числа с плавающей точкой (например, 5 + 0,1), компилятор автоматически преобразует целочисленное представление в представление с плавающей точкой, чтобы не терять дроби. Явные преобразования типов указываются либо путем написания дополнительного кода (например, добавления идентификаторов типов или вызова встроенных процедур ), либо путем кодирования процедур преобразования для использования компилятором, когда он в противном случае остановился бы из-за несоответствия типов.

В большинстве языков типа ALGOL , таких как Pascal , Modula-2 , Ada и Delphi , преобразование и приведение — это совершенно разные понятия. В этих языках преобразование относится либо к неявному, либо к явному изменению значения из одного формата хранения типа данных в другой, например, 16-битного целого числа в 32-битное целое число. Потребности в хранении могут измениться в результате преобразования, включая возможную потерю точности или усечение. С другой стороны, слово приведение относится к явному изменению интерпретации битовой комбинации, представляющей значение из одного типа в другой. Например, 32 смежных бита могут рассматриваться как массив из 32 булевых значений, 4-байтовая строка, беззнаковое 32-битное целое число или значение с плавающей точкой одинарной точности IEEE. Поскольку сохраненные биты никогда не изменяются, программист должен знать детали низкого уровня, такие как формат представления, порядок байтов и потребности выравнивания, для осмысленного приведения.

В семействе языков C и ALGOL 68 слово cast обычно относится к явному преобразованию типа (в отличие от неявного преобразования), что приводит к некоторой двусмысленности относительно того, является ли это переинтерпретацией битовой последовательности или реальным преобразованием представления данных. Более важным является множество способов и правил, которые применяются к тому, какой тип данных (или класс) находится указателем, и как указатель может быть скорректирован компилятором в таких случаях, как наследование объекта (класса).

Явное использование на разных языках

Ада

Ada предоставляет универсальную библиотечную функцию Unchecked_Conversion. [8]

C-подобные языки

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

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

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

двойной d ; длинный l ; целочисленный i ;   если ( d > i ) d = i ; если ( i > l ) l = i ; если ( d == l ) d *= 2 ;                  

Хотя d , l и i принадлежат к разным типам данных, они будут автоматически преобразованы в одинаковые типы данных каждый раз при выполнении сравнения или присваивания. Это поведение следует использовать с осторожностью, так как могут возникнуть непреднамеренные последствия . Данные могут быть потеряны при преобразовании представлений из числа с плавающей точкой в ​​целое число, так как дробные компоненты значений с плавающей точкой будут усечены (округлены до нуля). И наоборот, точность может быть потеряна при преобразовании представлений из целого числа в число с плавающей точкой, так как тип с плавающей точкой может быть неспособен точно представить все возможные значения некоторого целочисленного типа. Например, может быть типом одинарной точности IEEE 754 , который не может точно представить целое число 16777217, в то время как 32-битный целочисленный тип может. Это может привести к неинтуитивному поведению, как показано в следующем коде:float

#include <stdio.h> int main ( void ) { int i_value = 16777217 ; float f_value = 16777216.0 ; printf ( "Целое число равно: %d \n " , i_value ); printf ( "Плавающее число равно: %f \n " , f_value ); printf ( "Их равенство: %d \n " , i_value == f_value ); }                 

На компиляторах, реализующих float как числа с одинарной точностью IEEE, а int как минимум 32-битные, этот код выдаст такую ​​необычную распечатку:

Целое число: 16777217Число с плавающей точкой: 16777216.000000Их равенство: 1

Обратите внимание, что 1 представляет равенство в последней строке выше. Это странное поведение вызвано неявным преобразованием в i_valuefloat при сравнении с f_value. Преобразование приводит к потере точности, что делает значения равными до сравнения.

Важные выводы:

  1. floatвызывает intусечение , т.е. удаление дробной части.
  2. doubleвызывает floatокругление цифры.
  3. longвызывает intотбрасывание лишних битов более высокого порядка.
Тип продвижения

Одним из особых случаев неявного преобразования типа является повышение типа, когда объект автоматически преобразуется в другой тип данных, представляющий надмножество исходного типа. Повышение обычно используется с типами, меньшими, чем собственный тип арифметико-логического устройства (АЛУ) целевой платформы, перед арифметическими и логическими операциями, чтобы сделать такие операции возможными или более эффективными, если АЛУ может работать с более чем одним типом. C и C++ выполняют такое повышение для объектов булевых, символьных, широких символьных, перечислений и коротких целочисленных типов, которые повышаются до int, и для объектов типа float, которые повышаются до double. В отличие от некоторых других преобразований типов, повышение никогда не теряет точность и не изменяет значение, хранящееся в объекте.

На Яве :

int x = 3 ; double y = 3.5 ; System.out.println ( x + y ) ; // Вывод будет 6.5         

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

Явное преобразование типов, также называемое приведением типов, — это преобразование типов, которое явно определено в программе (вместо того, чтобы выполняться автоматически в соответствии с правилами языка для неявного преобразования типов). Оно запрашивается пользователем в программе.

double da = 3.3 ; double db = 3.3 ; double dc = 3.4 ; int result = ( int ) da + ( int ) db + ( int ) dc ; // result == 9 // если бы использовалось неявное преобразование (как в случае "result = da + db + dc"), результат был бы равен 10                 

Существует несколько видов явного преобразования.

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

В объектно-ориентированных языках программирования объекты также могут быть преобразованы  : ссылка на базовый класс преобразуется в один из его производных классов.

С# и С++

В C# преобразование типов может быть выполнено безопасным или небезопасным (т. е. C-подобным) способом, первый из которых называется проверяемым приведением типа . [9]

Животное животное = новый Кот ();    Бульдог b = ( Бульдог ) животное ; // если (животное - Бульдог), stat.type(животное) - Бульдог, иначе исключение b = животное как Бульдог ; // если (животное - Бульдог), b = (Бульдог) животное, иначе b = null          животное = null ; b = животное как Бульдог ; // b == null       

В C++ аналогичного эффекта можно добиться, используя синтаксис приведения в стиле C++ .

Животное * животное = новый Кот ;    Bulldog * b = static_cast < Bulldog *> ( animal ); // компилируется только если либо Animal, либо Bulldog являются производными от другого (или одного и того же) b = dynamic_cast < Bulldog *> ( animal ); // если (animal is Bulldog), b = (Bulldog*) animal, иначе b = nullptr       Bulldog & br = static_cast < Bulldog &> ( * animal ); // то же, что и выше, но будет выдано исключение, если должен быть возвращен nullptr // это не наблюдается в коде, где избегается обработка исключений     удалить животное ; // всегда свободные ресурсы животное = nullptr ; b = dynamic_cast < Bulldog *> ( животное ); // b == nullptr       

Эйфелева

В Eiffel понятие преобразования типа интегрировано в правила системы типов. Правило присваивания гласит, что присваивание, например:

х := у  

является допустимым, если и только если тип его исходного выражения, 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 — Собственная строка Eiffel my_system_string : SYSTEM_STRING — Собственная строка .NET      ... моя_строка := моя_системная_строка  

В коде выше объявлены две строки, по одной каждого типа ( SYSTEM_STRING— это совместимый с Eiffel псевдоним для System.String). Поскольку System.Stringне соответствует STRING_8, то указанное выше назначение допустимо только в том случае, если System.Stringпреобразуется в STRING_8.

Класс Eiffel STRING_8имеет процедуру преобразования make_from_cilдля объектов типа System.String. Процедуры преобразования также всегда обозначаются как процедуры создания (аналогично конструкторам). Ниже приведен фрагмент из STRING_8класса:

 класс STRING_8 ... создать make_from_cil ... преобразовать make_from_cil ({ SYSTEM_STRING }) ...         

Наличие процедуры преобразования делает назначение:

 моя_строка := моя_системная_строка  

семантически эквивалентно:

 создать my_string . make_from_cil ( my_system_string )  

в котором my_stringсоздается как новый объект типа STRING_8с содержимым, эквивалентным содержимому my_system_string.

Чтобы выполнить задание, в котором исходный источник и цель поменялись местами:

 моя_системная_строка := моя_строка  

класс STRING_8также содержит запрос преобразования to_cil, который создаст System.Stringиз экземпляра STRING_8.

 класс STRING_8 ... создать make_from_cil ... преобразовать make_from_cil ({ SYSTEM_STRING }) в_cil : { SYSTEM_STRING } ...           

Задание:

 моя_системная_строка := моя_строка  

тогда становится эквивалентным:

 my_system_string := my_string . to_cil  

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

Ржавчина

Rust не обеспечивает неявного преобразования типов (приведения) между примитивными типами. Но явное преобразование типов (приведение) может быть выполнено с использованием asключевого слова. [10]

пусть x = 1000 ; println! ( "1000 как u16 это: {}" , x как u16 );      

Утверждение типа

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

Машинопись

В TypeScript утверждение типа выполняется с помощью asключевого слова: [11]

const myCanvas = document.getElementById ( " main_canvas " ) as HTMLCanvasElement ;     

В приведенном выше примере document.getElementByIdобъявлено, что возвращает HTMLElement, но вы знаете, что он всегда возвращает HTMLCanvasElement, который является подтипом HTMLElement, в данном случае. Если это не так, последующий код, который полагается на поведение , HTMLCanvasElementне будет работать правильно, так как в Typescript нет проверки времени выполнения для утверждений типа.

В Typescript нет общего способа проверить, имеет ли значение определенный тип во время выполнения, поскольку отсутствует поддержка типов во время выполнения. Однако можно написать пользовательскую функцию, с помощью которой пользователь сообщает компилятору, имеет ли значение определенный тип или нет. Такая функция называется type guard и объявляется с возвращаемым типом x is Type, где x— параметр или this, вместо boolean.

Это позволяет содержать небезопасные утверждения типов в функции проверки, а не разбрасывать их по кодовой базе.

Идти

В Go утверждение типа может использоваться для доступа к конкретному значению типа из значения интерфейса. Это безопасное утверждение, что оно вызовет панику (в случае одного возвращаемого значения) или вернет нулевое значение (если используются два возвращаемых значения), если значение не принадлежит к этому конкретному типу. [12]

т := я .( Т )  

Утверждения этого типа сообщают системе, что iтип T. Если это не так, она паникует.

Неявное приведение с использованием немаркированных объединений

Многие языки программирования поддерживают типы объединений , которые могут содержать значение нескольких типов. Немаркированные объединения предусмотрены в некоторых языках со слабой проверкой типов, таких как C и PL/I , но также и в оригинальном Pascal . Их можно использовать для интерпретации битовой комбинации одного типа как значения другого типа.

Проблемы безопасности

В хакерстве приведение типов — это неправильное использование преобразования типов для временного изменения типа данных переменной по сравнению с тем, как он был изначально определен. [13] Это дает хакерам возможности, поскольку при преобразовании типов после того, как переменная «приводится к типу» и становится другим типом данных, компилятор будет рассматривать эту взломанную переменную как новый тип данных для этой конкретной операции. [14]

Смотрите также

Ссылки

  1. ^ abc Mehrotra, Dheeraj (2008). Компьютерная наука С. Чанда . С. Чанд. стр. 81–83. ISBN 978-8121929844.
  2. ^ Языки программирования - Проектирование и конструкции . Laxmi Publications. 2013. стр. 35. ISBN 978-9381159415.
  3. ^ ab Reilly, Edwin (2004). Краткая энциклопедия компьютерных наук. John Wiley & Sons. стр. 82, 110. ISBN 0470090952.
  4. ^ Фентон, Стив (2017). Pro TypeScript: Разработка JavaScript-приложений . Apress. стр. xxiii. ISBN 978-1484232491.
  5. ^ "PHP: Type Juggling - Manual". php.net . Получено 27 января 2019 г. .
  6. ^ ab Olsson, Mikael (2013). C++ Quick Syntax Reference . Apress. стр. 87–89. ISBN 978-1430262770.
  7. ^ Крузе, Рудольф; Боргельт, Кристиан; Брауне, Кристиан; Мостагим, Саназ; Штайнбрехер, Матиас (16 сентября 2016 г.). Вычислительный интеллект: методологическое введение . Спрингер. п. 269. ИСБН 978-1447172963.
  8. ^ "Непроверенные преобразования типов". Ada Information Clearinghouse . Получено 11 марта 2023 г.
  9. Мессенбёк, Ханспетер (25 марта 2002 г.). «Продвинутый C#: проверенное приведение типов» (PDF) . Институт системного программного обеспечения, Университет Иоганна Кеплера в Линце, Факультет информатики. п. 5 . Проверено 4 августа 2011 г.в C# Учебник
  10. ^ "Литье - Rust на примере". doc.rust-lang.org .
  11. ^ ""Машинопись документации"".
  12. ^ ""Тур по Го"".
  13. ^ Джон Эриксон Хакинг, 2-е издание: Искусство эксплуатации 2008 1593271441 стр. 51 «Приведение типов — это просто способ временно изменить тип данных переменной, несмотря на то, как он был изначально определен. Когда переменная приводится к другому типу, компилятору по сути говорят обращаться с этой переменной так, как если бы это был новый тип данных, но только для этой операции. Синтаксис приведения типов следующий: (typecast_data_type) переменная ...»
  14. ^ Arpita Gopal Magnifying C 2009 8120338618 стр. 59 "Из вышесказанного ясно, что использование приведения типов заключается в том, чтобы заставить переменную одного типа действовать как другой тип для одной единственной операции. Таким образом, используя эту возможность приведения типов, можно создавать символы ASCII путем приведения типа целого числа к его ..."

Внешние ссылки