Подтвердить что ты не робот

Целочисленные преобразования (сужение, расширение), поведение undefined

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


В примерах я буду иметь в виду:

(signed/unsigned) int bigger;
(signed/unsigned) char smaller;
  • Усечение целых чисел. (Bigger- > меньше)

    • сначала усечь bigger на стороне MSB, чтобы соответствовать размеру smaller.
    • второй, преобразовать сокращенный результат в подписанный/неподписанный в зависимости от меньшего типа.


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

  • Расширение целых чисел (меньше → больше)

    a) signed charsigned int

    • добавить меньше с MSB (1 или 0), чтобы соответствовать большему размеру
    • конвертировать в подписанный

    b) signed charunsigned int

    • добавить меньший размер с MSB (1 или 0), чтобы соответствовать большему размеру.
    • конвертировать в unsigned

    c) unsigned charsigned int

    • добавить с 0 для большего размера
    • конвертировать в подписанный

    d) unsigned charunsigned int

    • добавить с 0 для большего размера
    • конвертировать в unsigned

Где undefined/​​неуказанное поведение, о котором я не упоминал, может появиться?

4b9b3361

Ответ 1

Интегральное преобразование никогда не приводит к поведению undefined (он может создавать поведение, определенное реализацией).

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

Преобразование в неподписанный тип всегда корректно определено: значение принимается по модулю UINT_MAX + 1 (или любое другое максимальное значение, которое допускает тип цели).

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

Обратите внимание, что приведенные выше правила определены в терминах целочисленных значений, а не в терминах последовательностей бит.

Ответ 2

Из стандартного документа C (стр. 50 черновик версии 201x я верю, а не точную цитату):

  • Нет двух значащих целых чисел, имеющих одинаковый ранг

  • Ранг целого числа со знаком должен быть больше ранга любого знакового целого с меньшей точностью.

  • long long int больше длинного int, который больше, чем int, который больше, чем short int, который больше, чем подписанный char.

  • signed и unsigned с одинаковой точностью имеют один и тот же ранг (ex: signed int имеет тот же ранг, что и unsigned int)

  • Ранг любого стандартного целочисленного типа должен быть больше ранга любого расширенного целочисленного типа той же ширины.

  • Ранг char равен unsigned char равен подписанному char.

(Я ухожу из bool, потому что вы исключили их из своего вопроса)

  • Ранг любого расширенного целого числа со знаком по отношению к другому расширенному значащему целому числу определяется реализацией, но все еще подчиняется другим правилам целочисленного ранга преобразования.

  • для всех целых типов T1 T2 и T3, T1 имеет больший ранг, чем T2, а T2 имеет больший ранг, чем T3, чем T1 имеет больший ранг, чем T3.

Объект с целым типом (отличным от int и signed int), чей целочисленный ранг LESS или EQUAL для ранга int и unsigned int, битное поле типа _Bool, int, signed int или unsigned int; если int может представлять все значения исходного типа, значение преобразуется в int. В противном случае к unsigned int. Все остальные типы изменяются целым продвижением.

В простых выражениях:

Любой тип "меньше", чем int или unsigned int, передается в int при преобразовании в другой тип большего ранга. Это задание компилятора, чтобы убедиться, что код C, скомпилированный для данной машины (архитектуры), соответствует ISO-C в этом отношении. char - это реализация, определенная (подписанная или без знака). Все другие типы (продвижение по службе или "понижение в должности" ) определяются реализацией.

Что такое реализация? Это означает, что данный компилятор будет систематически вести себя одинаково на данной машине. Другими словами, все "поведение, определяемое реализацией" зависит от BOTH от компилятора AND target machine.

Сделать переносимый код:

  • всегда повышать ценности до более высоких стандартных типов C.
  • Никогда не уменьшайте значения до меньших типов.
  • Избегайте всех реализаций, реализованных в вашем коде.

Почему это безумие, определенное реализацией, существует, если оно разрушает усилия программистов??? Системное программирование в основном требует такого поведения, определенного для реализации.

Так более конкретно к вашему вопросу:

  • Усечение, скорее всего, не будет противочным. Или потребуется гораздо больше усилий в обслуживании, отслеживании ошибок и т.д., Чем просто поддерживать код, используя более высокие типы рангов.
  • Если ваша реализация использует значения, превышающие используемые типы, ваш дизайн неверен (если вы не участвуете в системном программировании).
  • Как правило, переход от unsigned to signed сохраняет значения, но не наоборот. Поэтому, когда значение без знака выходит на носок против подписанного, продвигайте без знака к подписанному, а не наоборот.
  • Если использование как можно большего числа целых чисел является критическим для памяти в вашем приложении, вероятно, вы должны пересмотреть всю архитектуру программы.