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

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


в примерах я буду ссылаться на:

(signed/unsigned) int bigger;
(signed/unsigned) char smaller;
  1. усек целых чисел. (больше-меньше)

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


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

  2. расширение целых чисел (меньше>больше)

    a)signed char ->signed int

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

    b)signed char ->unsigned int

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

    c)unsigned char ->signed int

    • prepend с 0's, чтобы соответствовать больше размер
    • преобразовать в signed

    d)unsigned char ->unsigned int

    • prepend с 0's, чтобы соответствовать большему размеру
    • преобразовать в unsigned

где неопределенные / неопределенные поведения, которые я не упомянул, которые могут всплывать?

2 ответов


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

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

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

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

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


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

  • ни одно целое число со знаком не должно иметь одинакового ранга

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

  • long long int больше, чем long int, который больше, чем int, который больше, чем short int, который больше, чем signed пеструшка.

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

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

  • ранг char равен unsigned char равен signed char.

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

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

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

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

в:

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

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

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

  • всегда продвигайте значения к типам стандарта C большего ранга.
  • никогда не" понижайте " значения до меньших типов.
  • избежать все "реализации" реализации в коде.

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

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

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