Что должен означать кастинг?

18

При кодировании на низкоуровневых языках, таких как CI, обнаруживается, что приведение иногда означает «переосмыслить эти байты, как если бы оно всегда имело этот другой тип», а в других случаях - как «преобразовать это значение разумно в этот другой тип».

Каково первоначальное значение слова и есть ли последовательность в том, когда ожидать преобразования и когда ожидать необработанной реинтерпретации?

Александр Торстлинг
источник
что в статье в Википедии, которую вы не понимаете? « Преобразование типов , приведение типов и принуждение - это различные способы явного или неявного изменения сущности одного типа данных в другой ... Каждый язык программирования имеет свои собственные правила преобразования типов ...»
gnat
Первоначальное значение «бросать» не имеет ничего общего с программированием, см. Здесь merriam-webster.com/dictionary/cast
Док Браун
1
Много (в основном с точки зрения управляемого языка) в блоге Эрика Липперта в категории операторов операторов
AakashM
1
@gnat: Я не уверен, что это серьезный вопрос или просто попытка троллинга. Но я хотел бы знать, как узнать, что будет делать компилятор: конвертировать, приводить или приводить? Каковы правила большого пальца?
Александр Торстлинг
1
@DocBrown Я думаю, что термин castв вычислительном смысле больше похож на отливку в металлургическом смысле, когда форма расплавленного металла преобразуется при заливке в форму: britannica.com/EBchecked/topic/377665/metallurgy/81884/Casting
KChaloux

Ответы:

10

Кастинг в Си уникален, совсем не похож на другие языки. Это также никогда не разумно.

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

  1. Преобразование между целочисленными типами сохраняет значение, если это возможно. Если у пункта назначения больше битов, это расширение и, как правило, безопасно, но может включать расширение знака. Если уже, биты будут потеряны.
  2. Преобразование между типами указателей сохраняет значение указателя, но результаты часто не определены, часто непереносимы и часто полезны для сложных сценариев.
  3. Преобразование между целочисленными типами и указателями в порядке, если целое число достаточно велико и сохраняет битовый шаблон (что бы это ни значило). Если целое число слишком мало, результат не определен, но бесполезен. Как правило, «long» достаточно широк для «void *», но никаких гарантий! Указатели, созданные таким образом, могут быть недействительными, во всех видах интересных способов.
  4. Преобразование между типами с плавающей точкой и целочисленными типами - это арифметические преобразования, как определено соответствующей библиотечной подпрограммой (с усечением, а не с округлением).
  5. Вы можете привести возвращаемое значение функции к void. У меня никогда не было. Это ничего не делает.

Некоторые приведения применяются неявно, а в некоторых из них компилятор выдает предупреждение. Лучше всего прислушаться к предупреждениям!

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

С ++ НАМНОГО сложнее, но ты об этом не спрашивал?

david.pfx
источник
Я заинтересован в эмпирических правилах, а не в мельчайших деталях, но меня интересуют другие языки, кроме C, если это помогает прояснить ситуацию.
Александр Торстлинг
2
То, что я дал здесь, является настолько общим, насколько это разумно. Для написания реального низкоуровневого профессионального кода на C / C ++ крайне важны мелкие детали. Большинство языков просто не имеют такой проблемы при преобразовании типов. Извините, если это не решит вашу проблему.
david.pfx
За исключением того, что преобразование из T*туда void*и обратно всегда четко определено.
Майлз Рут
@Miles: на самом деле преобразование T * в любое U * и обратно необходимо для сохранения исходного значения указателя. В своем ответе я только сказал «часто неопределенный», чтобы сократить его, потому что некоторые детали очень грязные.
david.pfx
1
@supercat: см. n1570 S6.3.2.3. Преобразование / циклическое переключение между T *, U * и void * всегда сохраняет значение указателя только с одним исключением. Если какой-либо T * не выровнен правильно, это неопределенное поведение. Я принимаю вашу точку зрения, но только до такой степени.
david.pfx
2

Эта часть словаря Вебстера дает правильное определение:

а: для придания формы (вещества) путем заливки в форме жидкости или пластика в форму и оставления затвердеть без давления;
б: для образования этого процесса

Таким образом, перед приведением в действие ваш «объект» (не буквально объект ООП) имеет заданную форму (тип). Когда вы заново его заливаете, это «заливает бетон» вокруг него, чтобы придать ему новую форму, это то, что вы делаете с заливкой. У вас есть число в виде шестиугольника в виде целого числа, и после приведения вы получите строку в форме прямоугольника.

Юха Унтинен
источник
2
Также : «Назначить определенную роль (актеру)».
Келли Томас
Ага. Я уверен, что это лучшее. +1.
david.pfx
2

Может быть полезно разделить приведение C на две группы:

  1. Числовое приведение - конвертируйте число между одним представлением в другое, пытаясь сохранить значение. Например - (int)3.1будет 3. Существуют точные правила, определяющие, что происходит, когда точное значение не может быть сохранено.

  2. Приведение указателя - сохраните адрес памяти, но измените способ разыменования. Например, for float x=3.5, *(int *)&xдаст 1080033280- это целое число представлено тем же битовым шаблоном, который представляет число с плавающей точкой 3.5.

ugoren
источник
Keep the memory address, but change the way it's dereferenced.Разыменование указателя типа-не определено. Стандарт только гарантирует, что приведение A *к B *и обратно будет производить то же самое A *, что, возможно, было недопустимо для разыменования в 1-м месте - или что, если B *это char *, он может использоваться для чтения представления объекта любого типа. Для всех других типов B *указатель разыменования имеет тип-пробивание, UB и нарушает строгое псевдоним. В любом случае, даже если по этой причине компилятор не испортил вышеприведенный пример 2, вы делаете непереносимые предположения о битовых шаблонах
underscore_d
1
cast (v): to receive form in a mold

В C ++ различные типы приведений могут быть сделаны более явными, что reinterpret_castозначает «обрабатывать эти байты, как если бы они уже были этим другим». В C вы можете сделать это абсолютно явным, используя a union, приведение с (type)оператором попытается сохранить результат численно эквивалентным, вплоть до потери точности.

U2EF1
источник
2
В C приведение указателей всегда переинтерпретируется, а приведение значений всегда сохраняет значение как можно лучше. В C ++ существует несколько способов приведения указателей, поэтому существуют более явные типы приведения.
Ян Худек
Семантика приведения указателя C не обязательно "переосмыслена". Было бы законно, если бы процессор, который использовал адресацию слов, но хотел хорошо взаимодействовать с байтовым кодом, чтобы иметь int*одно слово, а char*двух - слово [со вторым байтом, выбирающим старший или младший байт слова]. Преобразование в (int*)to (char*)потребует добавления дополнительного слова, которое должно быть любым значением, которое будет указывать первый байт int.
суперкат