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

Из Википедии, бесплатной энциклопедии

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

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

Есть [ править ]

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

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

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

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

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

двойной    д  ; 
  длинный      л  ; 
  ИНТ       я  ; 

  если   (  d   >   я  )     d   знак равно   я  ; 
  если   (  я   >   л  )     л   знак равно   я  ; 
  если   (  d   ==   л  )    d   *=   2  ; 

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

#include   <stdio.h> 

 int   main  (  void  ) 
 { 
     int   i_value     =   16777217  ; 
      поплавок   f_value   =   16777216.0  ; 
      printf  (  "Целое число: %d  \n  "  ,   i_value  ); 
      printf  (  "Число с плавающей запятой: %f  \n  "  ,   f_value  ); 
      printf  (  "Их равенство: %d  \n  "  ,   i_value   ==   f_value  ); 
  } 

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

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

Обратите внимание, что 1 представляет равенство в последней строке выше. Такое странное поведение вызвано неявным преобразованием i_value плавать, когда его сравнивают с f_value. Преобразование приводит к потере точности, в результате чего значения перед сравнением становятся равными.

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

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

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

На Яве :

интервал   х   =   3  ; 
  двойной   у   =   3,5  ; 
  Система  .   вне  .   печатьln  (  х   +   у  );    // Результат будет 6,5 

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

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

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

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

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

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

C# и C++ [ править ]

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

Животное   животное   =   новый   Кот  (); 

  Бульдог   b   =   (  бульдог  )   животное  ;     // if (животное — Bulldog), stat.type(animal) — Bulldog, иначе — исключение 
 b   =   животное   как   Bulldog  ;            // if (животное — бульдог), b = (бульдог) животное, else b = null 

 Animal   =   null  ; 
  б   =   животное   как   бульдог  ;            // б == ноль 

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

Животное  *   животное   =   новый   Кот  ; 

  Bulldog  *   b   =   static_cast  <  Bulldog  *>  (  животное  );    // компилируется только в том случае, если Animal или Bulldog получен из другого (или одного и того же) 
 b   =   Dynamic_cast  <  Bulldog  *>  (  Animal  );            // if (животное — бульдог), b = (Бульдог*) животное, else b = nullptr 

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

 delete   Animal  ;    // всегда свободные ресурсы 
 Animal   =   nullptr  ; 
  b   =   Dynamic_cast  <  Бульдог  *>  (  животное  );            // b == nullptr 

Эйфелева [ править ]

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

х   :=   у 

допустимо тогда и только тогда, когда тип его исходного выражения, 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 

         ... 

             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 
         ... 
     создать 
         make_from_cil 
         ... 
     преобразовать 
         make_from_cil   ({  SYSTEM_STRING  }) 
         ... 

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

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

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

            создать   мою_строку  .   make_from_cil   (  my_system_string  ) 

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

Чтобы обработать назначение с обратным исходным источником и целью:

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

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

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

Назначение:

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

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

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

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

Ржавчина [ править ]

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

пусть   х   =   1000  ; 
  распечататьлн!   (  "1000 как u16: {}"  ,   x   как   u16  ); 

Тип утверждения [ править ]

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

Машинопись [ править ]

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

const   myCanvas   =   документ  .   getElementById  (  "main_canvas"  )   как   HTMLCanvasElement  ; 

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

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

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

Иди [ править ]

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

т   :=   я  .(  Т  ) 

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

Неявное приведение с использованием объединений без тегов [ править ]

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

Проблемы безопасности [ править ]

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

См. также [ править ]

Ссылки [ править ]

  1. ^ Перейти обратно: а б с Мехротра, Дирадж (2008). Информатика С. Чанда . С. Чанд. стр. 100-1 81–83. ISBN  978-8121929844 .
  2. ^ Языки программирования — Проектирование и конструкции . Публикации Лакшми. 2013. с. 35. ISBN  978-9381159415 .
  3. ^ Перейти обратно: а б Рейли, Эдвин (2004). Краткая энциклопедия информатики . Джон Уайли и сыновья. стр. 82, 110 . ISBN  0470090952 .
  4. ^ Фентон, Стив (2017). Pro TypeScript: разработка JavaScript в масштабе приложения . Апресс. стр. XXII. ISBN  978-1484232491 .
  5. ^ «PHP: Жонглирование типами — Руководство» . php.net . Проверено 27 января 2019 г.
  6. ^ Перейти обратно: а б Олссон, Микаэль (2013). Краткий справочник по синтаксису C++ . Апресс. стр. 87–89. ISBN  978-1430262770 .
  7. ^ Крузе, Рудольф; Боргельт, Кристиан; Брауне, Кристиан; Мостагим, Саназ; Штайнбрехер, Матиас (16 сентября 2016 г.). Вычислительный интеллект: методологическое введение . Спрингер. п. 269. ИСБН  978-1447172963 .
  8. ^ «Непроверенные преобразования типов» . Информационный центр Ады . Проверено 11 марта 2023 г.
  9. ^ Мессенбёк, Ханспетер (25 марта 2002 г.). «Продвинутый C#: проверенное приведение типов» (PDF) . Институт системного программного обеспечения, Университет Иоганна Кеплера, Линц, факультет компьютерных наук. п. 5 . Проверено 4 августа 2011 г. в учебнике по C#
  10. ^ «Литье — ржавчина на примере» . doc.rust-lang.org .
  11. ^ " "Машинописная документация" " .
  12. ^ « Экскурсия по Го » .
  13. ^ Jon Erickson Hacking, 2nd Edition: The Art of Exploitation 2008 1593271441 p51 «Приведение типов — это просто способ временно изменить тип данных переменной, несмотря на то, как он был первоначально определен. Когда переменная приводится к другому типу, компилятор по сути сказано обрабатывать эту переменную так, как если бы это был новый тип данных, но только для этой операции. Синтаксис приведения типов следующий: (typecast_data_type) переменная ..."
  14. ^ Арпита Гопал Лупа C 2009 8120338618 стр. 59 «Из вышеизложенного ясно, что использование приведения типов заключается в том, чтобы заставить переменную одного типа действовать как другой тип для одной операции. Таким образом, используя эту возможность приведения типов, можно создавать символы ASCII путем приведения целого числа к типу. его ..."

Внешние ссылки [ править ]