Правильный способ объединения побитовых операций?

#c

#c

Вопрос:

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

 unsigned char a = 0x12
unsigned char x = 0x00;
x = a << 4;
x = x >> 4;
 

ожидаемый результат x = 0x02;
текущий результат x = 0x02;

Если я попытаюсь объединить операции, результат будет неверным:

 unsigned char a = 0x12
unsigned char x = 0x00;
x = (a << 4) >> 4;
 

ожидаемый результат x = 0x02;
текущий результат x = 0x12;

Заранее спасибо за любые предложения.

Комментарии:

1. Что вы подразумеваете под «конкатенацией»? Чего вы ожидаете?

2. Результатом a << 4 будет an int , поэтому вы должны отбросить его назад раньше >> 4 , т. е. x = static_cast<unsigned char>(a << 4) >> 4; .

3. Это мое воображение, или вы выполнили операцию, отменили эту операцию, а затем ожидаете, что она выведет что-то отличное от оригинала. Здесь есть несколько хороших попыток найти ответы, так что я, вероятно, что-то упускаю. Можете ли вы (или кто-нибудь) объяснить, почему вы думаете, что это будет что-то другое, чем 0x12 ?

4. @Chipster 0x12 << 4 был 0x120 бы, но он усекается 0x20 при сохранении как an unsigned char , поэтому его возврат только возвращает 0x02 , а не 0x12 . ответы объясняют, почему усечение происходит в случае 1, но не в случае 2

5. @kmdreko Ах, потому что теоретически это всего лишь символ. Понял тебя. Теперь это имеет смысл.

Ответ №1:

Проблема (a << 4) заключается в приведении к int (через интегральное продвижение), поэтому (0x12 << 4) >> 4 по сути 0x12

Что вы хотите сделать, это преобразовать обратно (a << 4) unsigned char , используя static_cast

Окончательный код:

    unsigned char a = 0x12;
   unsigned char x = 0x00;
   x = static_cast<unsigned char>(a << 4) >> 4;   
 

Комментарии:

1. @Chipster суть в том, что он надеется, поскольку char равен 8 битам, a << 4 дать 2, поскольку 1 превышает 8 битный размер. Жаль, что операция выполняется в int, и она никогда не стирается

2. спасибо Мартину М. за ваше объяснение. я буду использовать ваше решение.

3. @Gardener если бы компилятор замаскировал здесь младшие 8 бит, даже на 8-разрядной машине, это было бы нарушением правил языка C , в частности [expr.shift]/1 , [conv.prom]/1 и [basic.fundamental]/4

Ответ №2:

Компилятор НЕ применяет интегральные поощрения для операций >> и <<

Вы можете подумать, что

 x = (a << 4) >> 4;
 

Для операции использовался бы регистр шириной в байт, но компилятор преобразует char a в int перед выполнением сдвига, сохраняя биты, сдвинутые влево.

Вы можете решить эту проблему, выполнив это:

 x = ((a << 4) amp; 0xff) >> 4;
 

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

Комментарии:

1. Пожалуйста, обратите внимание, что проблема здесь не в том, что компилятор ничего не смог сделать. Проблема в том, что ожидания человека, написавшего этот код, не совпали с реальностью. По правилам языка C , операнды для встроенных >> << операторов и подлежат целому продвижению. Компилятор сделал именно то, что он всегда должен был делать, даже на 8-разрядном компьютере…

2. спасибо за ваше объяснение, да, я не знал о продвижении integral.