2013-04-18 3 views
1

Это своего рода вождение меня с ума.Бит сдвига и назначение

int a = 0xffffffff; 
int b = 32; 
cout << (a << b) << "\n"; 
cout << (0xffffffff << 32) << "\n"; 

Мой выход

-1 
0 

Почему я не получаю

0 
0 
+0

http://stackoverflow.com/questions/6916974/change-a-bit-of-an-integer Я люблю ^^^ этот пост, так что у меня есть ссылка в избранное. Надеюсь, это прояснит бит. Это может быть очень запутанным. –

+0

Первый не работает, потому что 'a' - целое число со знаком, которое может использовать только 31 своих битов для представления числа. Второй работает, потому что тип '0xffffffff' повышается до' unsigned int', который использует все 32 бита. Перемещение больше, чем выделенные биты для определенного типа 'signed' - это неопределенное поведение, в то время как для шестнадцатеричного литерала, который является' unsigned', поведение определено и работает так, как ожидалось. – 0x499602D2

ответ

8

Неопределенное поведение происходит, когда вы сдвигаете значение на число битов, которое не меньше, чем его размер (например, 32 или более бит для 32-разрядного целого числа). Вы только что столкнулись с примером этого неопределенного поведения.

+0

Большое спасибо, я так долго задерживался на этом. – newprogrammer

+0

Кроме того, подписанное целочисленное переполнение является неопределенным поведением: 'int a = 0xffffffff;' – Sebivor

9

Короткий ответ заключается в том, что, поскольку вы используете реализацию с 32-битным int, в стандарте языка говорится, что 32-разрядный сдвиг является неопределенным поведением. Как стандарт C (раздел 6.5.7), и стандартный С ++ (раздел 5.8) говорят

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

Но если вы хотите знать, почему :

Многие компьютеры имеют инструкцию, которая может сдвинуть значение в регистре, но оборудование обрабатывает только значения смещения, которые действительно необходимы. Например, при перемещении 32-битного слова требуется только 5 бит, чтобы представить значение сдвига 0 ... 31, и поэтому аппаратное обеспечение может игнорировать биты более высокого порядка и делает на * 86 машинах (за исключением 8086). Так что реализации компилятора могли бы просто использовать инструкцию без генерации дополнительного кода, чтобы проверить, слишком ли велика величина сдвига, авторы стандарта C (многие из которых представляли поставщиков компилятора) постановили, что результат перехода на большие суммы не определен.

Ваша первая смена выполняется во время выполнения и встречается с этой ситуацией ... только ваш младший бит 5 бит b рассматривается вашей машиной, и они равны 0, поэтому сдвиг не происходит. Вторая смена выполняется во время компиляции, и компилятор вычисляет значение по-разному и фактически выполняет 32-битный сдвиг.

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

#define LEFT_SHIFT(a, b) ((b) >= CHAR_BIT * sizeof(a)? 0 : (a) << (b)) 
+0

+1 для различия между оценкой компиляции и временем выполнения. –

+0

@JonathanLeffler Конечно, компилятор * мог бы * выполнить оба вычисления во время компиляции, учитывая код OP, но, видимо, этого не делал. –

+0

Хороший ответ. Я всегда ценю это, когда люди выходят за рамки минимума и вникают под капот. Благодарю. – Mordachai

0

C++ говорит :: Если значение правого операнда отрицателен или больше или равна ширине продвигаемого левого операнда, поведение не определено. Поскольку GCC не имеет возможности обрабатывать сдвиги отрицательными суммами или суммами вне ширины типа прогнозируемого или ловушки на них; они всегда рассматриваются как неопределенные. Так поведение не определено.