Почему (1 >> 0x80000000) == 1?

Число 1, сдвинутое справа на что-либо большее, чем 0, должно быть 0, правильно? Тем не менее, я могу ввести эту очень простую программу, которая печатает 1.

#include <stdio.h>

int main()
{
        int b = 0x80000000;
        int a = 1 >> b;
        printf("%d\n", a);
}

Протестировано с помощью gcc на linux.

Ответы

Ответ 1

6.5.7 Операторы побитового сдвига:

Если значение правильного операнда отрицательное или больше или равно ширине продвинутого левого операнда, поведение undefined.

У компилятора есть лицензия, чтобы сделать что-либо, очевидно, но наиболее распространенные действия - полностью оптимизировать выражение (и все, что от него зависит), или просто позволить базовому оборудованию делать то, что он делает для вне- сдвиг диапазона. Многие аппаратные платформы (включая x86 и ARM) маскируют некоторое количество младших разрядов для использования в качестве суммы сдвига. Фактическая аппаратная инструкция даст результат, который вы наблюдаете на любой из этих платформ, поскольку сумма сдвига маскируется до нуля. Поэтому в вашем случае компилятор мог бы оптимизировать сдвиг, или это может просто позволить аппаратным средствам делать то, что он делает. Осмотрите сборку, если хотите узнать, какой из них.

Ответ 2

в соответствии со стандартом, сдвиг более чем на фактически существующие биты может привести к поведению undefined. Поэтому мы не можем обвинять компилятор в этом.

Мотивация, вероятно, находится в "значении границы" 0x80000000, который сидит на границе максимального положительного и отрицательного вместе (и это "отрицательный", имеющий самый высокий набор бит), и при определенной проверке, которая должна быть выполнена, и что скомпилированная программа не должна не терять времени, проверяя "невозможные" вещи (действительно ли вы хотите, чтобы процессор смещал биты в 3 миллиарда раз?).

Ответ 3

Скорее всего, это не попытка сдвигаться на некоторое количество бит.

INT_MAX в вашей системе, вероятно, 2**31-1, или 0x7fffffff (я использую ** для обозначения экспоненциальности). Если это случай, то в объявлении:

int b = 0x80000000;

(в котором не было точки с запятой в вопросе, скопируйте и вставьте свой точный код) константа 0x80000000 имеет тип unsigned int, а не int. Значение неявно преобразуется в int. Поскольку результат выходит за пределы int, результат определяется реализацией (или, в C99, может повысить сигнал, определенный реализацией, но я не знаю никакой реализации, которая делает это).

Самый распространенный способ сделать это - переосмыслить биты неподписанного значения как значащее значение 2-го уровня. Результатом в этом случае является -2**31 или -2147483648.

Таким образом, поведение не undefined, потому что вы смещаетесь по значению, которое равно или превышает ширину типа int, оно undefined, потому что вы смещаетесь на (очень большое) отрицательное значение.

Не то чтобы это важно, конечно; undefined - undefined.

ПРИМЕЧАНИЕ. Вышеприведенное предполагает, что int - 32 бита в вашей системе. Если int шире 32 бита, то большинство из них не применяется (но поведение по-прежнему undefined).

Если вы действительно хотите попытаться сдвинуть бит 0x80000000, вы можете сделать это следующим образом:

unsigned long b = 0x80000000;
unsigned long a = 1 >> b;    // *still* undefined

unsigned long гарантированно будет достаточно большим, чтобы удерживать значение 0x80000000, поэтому вы избегаете части проблемы.

Конечно, поведение сдвига равно как и undefined, как это было в вашем исходном коде, так как 0x80000000 больше или равно ширине unsigned long. (Если ваш компилятор имеет действительно большой тип unsigned long, но никакой компилятор реального мира не делает этого.)

Единственный способ избежать поведения undefined - это не делать то, что вы пытаетесь сделать.

Возможно, , но исчезающе маловероятно,, что ваше исходное поведение кода не undefined. Это может произойти только в том случае, если преобразование 0x80000000 от реализации 0x80000000 от unsigned int до int дает значение в диапазоне 0.. 31. IF int меньше 32 бит, преобразование, вероятно, даст 0.

Ответ 4

хорошо читал, что может помочь вам

expression1 → expression2

Оператор → маскирует выражение2, чтобы избежать слишком большого смещения выражения1.

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

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

выражение mask2 (с использованием побитового оператора И) с одним меньшим числа бит в выражении1.

Пример

var x : byte = 15;
// A byte stores 8 bits.
// The bits stored in x are 00001111
var y : byte = x >> 10;
// Actual shift is 10 & (8-1) = 2
// The bits stored in y are 00000011
// The value of y is 3
print(y); // Prints 3

То, что "8-1" - это потому, что x равно 8 байтам, поэтому операция будет иметь 7 бит. что void удаляет последний бит исходных битов цепи