Почему (1 >> 0x80000000) == 1? - PullRequest
14 голосов
/ 30 сентября 2011

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

#include <stdio.h>

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

Протестировано с gcc на linux.

Ответы [ 4 ]

30 голосов
/ 30 сентября 2011

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

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

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

2 голосов
/ 30 сентября 2011

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

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

1 голос
/ 30 сентября 2011

Очень вероятно, не пытается сдвинуться на какое-то большое количество бит.

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

int b = 0x80000000;

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

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

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

Не то, чтобы это имело значение, конечно; undefined является неопределенным.

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

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

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

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

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

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

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

0 голосов
/ 30 сентября 2011

хорошо прочитайте, что, возможно, может помочь вам

expression1 >> expression2

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

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

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

маска expression2 (используя побитовый оператор AND)на единицу меньше, чем количество битов в выражении 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 битами.эта пустота удаляет последний бит оригинальных цепных битов

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