Почему сдвиг битов переменной и числа не дает одинакового результата? - PullRequest
2 голосов
/ 07 июня 2019

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

int a = 97;
int b = 0;

b = 1 << a;
printf("%d\n", b); 
// 2

b = 1 << 97;
printf("%d\n", b); 
// 0 - warning: shift count >= width of type [-Wshift-count-overflow]

Ответы [ 3 ]

4 голосов
/ 07 июня 2019

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

В случае переменной (1 << a), поскольку a равно 97 (больше, чем число битов в int), наиболее вероятные результаты: 1 << (97 % 32) == 1 << 1 == 2 или 0, обычно в зависимости от того, как аппаратное обеспечение (ЦП) обрабатывает эти сдвиги.

С константой (1 << 97) компилятор знает, что вы перемещаетесь слишком далеко, выдает предупреждение (которое не требуется) и определяет результат как 0 (также не требуется).

3 голосов
/ 07 июня 2019

Предупреждение, которое вы видите, является предупреждением времени компиляции.Теперь вы можете ясно видеть, что ваша int b - это 32-битная переменная, которая будет переполнена, если смещено влево 97 раз.Так что это действительная проблема.Но компилятор может обнаружить это переполнение только для постоянного числа смен, поскольку оно оценивается во время компиляции, и компилятор сразу же знает, что оно переполнится.

В случае переменного числа смен компилятор недостаточно умен, чтобы знать, какое значение будет иметь int a, когда дело доходит до сдвига.Итак, компилятор оставляет это на ваше усмотрение.

0 голосов
/ 07 июня 2019

Неопределенное поведение описано здесь в стандарте C ++.

http://eel.is/c++draft/expr.shift

Поведение не определено, если правый операнд отрицательный или больше или равенширина повышенного левого операнда.

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

Почему именно так оно и действует?Инструкция x86 для сдвига по переменной - SAL (shift-arithmetic-left).Вы можете увидеть список команд для операций сдвига битов здесь:

https://c9x.me/x86/html/file_module_x86_id_285.html

Тот, который будет использоваться в неоптимизированной сборке, будет SAL r/m32, CL.Регистр CL имеет 8 бит, но процессор внутренне маскирует его до 5 бит:

Операндом-адресатом может быть регистр или ячейка памяти.Операндом count может быть непосредственное значение или регистр CL.Счетчик маскируется до 5 битов, что ограничивает диапазон счетчика от 0 до 31. Для счетчика 1 предусмотрено специальное кодирование кода операции.

...