Сдвиги, типы и знаки расширения в Си

У меня есть следующий код:

unsigned char chr = 234; // 1110 1010
unsigned long result = 0;
result = chr << 24;

И теперь результат будет равен 18446744073340452864, что 1111 1111 1111 1111 1111 1111 1111 1111 1110 1010 0000 0000 0000 0000 0000 0000 в двоичном

Почему происходит расширение знака, когда chr не подписан?

Также, если я изменю сдвиг с 24 на 8, результат будет 59904, который 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 1110 1010 0000 0000 в двоичном Почему здесь не сделано расширение? (Любой сдвиг 23 или меньше не имеет расширения знака)

Также на моей нынешней платформе sizeof(long) это 8.

Каковы правила автоматического приведения к типу большего размера при переключении? Мне кажется, что если сдвиг равен 23 или меньше, то chr преобразуется в неподписанный тип, а если в 24 или более он преобразуется в подписанный тип? (И почему расширение знака вообще делается с левым сдвигом)

2 ответа

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

Каждый целочисленный тип имеет фиксированный диапазон представимых значений. Например, unsigned char обычно колеблется от 0 в 255; возможны другие диапазоны, и вы можете найти выбор вашего компилятора, проверив UCHAR_MAX в limits.h,

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

Если значение не представляется в типе назначения, то:

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

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


Переходя к вашему фактическому примеру.

В C есть то, что называется интегральной рекламой. С <<это происходит с левым операндом; с арифметическими операторами это происходит со всеми операндами. Эффект интегрального продвижения состоит в том, что любое значение типа меньше int преобразуется в то же значение с типом int,

Далее определение << 24 это умножение на 2^24 (где это имеет тип повышенного левого операнда), с неопределенным поведением, если это переполняется. (Неофициально: сдвиг в знаковый бит вызывает UB).

Итак, если выразить все преобразования явно, ваш код

result = (unsigned long) ( ((int)chr) * 16777216 )

Теперь результат этого расчета 3925868544 что, если вы находитесь на типичной системе с 32-разрядным int, больше, чем INT_MAX 2147483647, поэтому поведение не определено.

Если мы хотим изучить результаты этого неопределенного поведения в типичных системах: то, что может произойти, - это та же самая процедура, которую я описал ранее для назначения вне диапазона. Битовый паттерн 3925868544 конечно 1110 1010 0000 0000 0000 0000 0000 0000, Рассматривая это как образец int использование дополнения 2 дает инт -369098752,

Наконец, у нас есть преобразование этого значения в unsigned long, -369098752 вне диапазона для unsigned long; и правило для неподписанного места назначения - корректировать значение по модулю ULONG_MAX+1, Таким образом, ценность, которую вы видите 18446744073709551615 + 1 - 369098752,

Если вы намеревались сделать расчет в unsigned long точность, вам нужно сделать один из операндов unsigned long; например сделать ((unsigned long)chr) << 24, (Заметка: 24ul не будет работать, тип правого операнда << или же >> не влияет на левый операнд).

С chr = 234, выражение chr << 24 оценивается изолированно: chr повышен до (32-разрядная подпись) int и сдвинул влево на 24 бита, получив отрицательный int значение. При назначении на 64-битную unsigned longзнаковый бит распространяется через старшие 32 бита 64-битного значения. Обратите внимание, что метод расчета chr << 24 не зависит от того, чему присвоено значение.

Когда сдвиг составляет всего 8 бит, результатом является положительное (32-разрядное знаковое) целое число, и этот знаковый бит (0) распространяется через наиболее значимые 32-разрядные числа unsigned long,

Другие вопросы по тегам