Операторы сдвига в C ++ - PullRequest
       27

Операторы сдвига в C ++

9 голосов
/ 30 марта 2010

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

Может кто-нибудь объяснить мне, что означают вышеуказанные строки ??

Ответы [ 6 ]

23 голосов
/ 30 марта 2010

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

"Если значение после оператора сдвига больше, чем количество бит в левый операнд, результат не определено. "

Верно, но должно сказать "больше или равно". 5,8 / 1:

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

Неопределенное поведение означает «не делай этого» (см. Позже). То есть, если int - это 32 бита в вашей системе, вы не можете выполнить одно из следующих действий:

int a = 0; // this is OK
a >> 32;   // undefined behavior
a >> -1;   // UB
a << 32;   // UB
a = (0 << 32); // Either UB, or possibly an ill-formed program. I'm not sure.

"Если левый операнд не подписан, правый сдвиг является логическим сдвигом, поэтому старшие биты будут заполнены нулями."

Это правда. 5.8 / 3 говорит:

Если E1 имеет тип без знака или E1 имеет тип со знаком и неотрицательное значение, результат является неотъемлемой частью отношение E1, деленное на количество 2 возведены в степень Е2

если это имеет больше смысла для вас. >>1 - это то же самое, что деление на 2, >>2 деление на 4, >>3 на 8 и так далее. В двоичном представлении положительного значения деление на 2 - это то же самое, что перемещение всех битов на один вправо, отбрасывание наименьшего бита и заполнение самого большого бита 0.

"Если левый операнд подписан, правый сдвиг может быть или не быть логическим сдвигом (то есть поведение не определено)."

Первая часть верна (это может быть или не быть логическим сдвигом - это на некоторых компиляторах / платформах, но не на других. Я думаю, что наиболее распространенным поведением является то, что это не так). Вторая часть неверна, поведение не определено. Неопределенное поведение означает, что разрешено все, что угодно - авария, демоны вылетающие из носа, случайное значение, что угодно. Стандарту все равно. Есть много случаев, когда стандарт C ++ говорит, что поведение не определено, но это не один из них.

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

Если левый операнд подписан, а значение отрицательное, то результирующее значение определяется реализацией. Не допускается падение или загорание. Реализация должна давать результат, а документация для реализации должна содержать достаточно информации, чтобы определить, каким будет результат. На практике «документация для реализации» начинается с документации компилятора, но она может явно или неявно ссылаться на другие документы для ОС и / или ЦП.

Опять из стандарта, 5,8 / 3:

Если E1 имеет тип со знаком и отрицательный значение, полученное значение реализации.

5 голосов
/ 30 марта 2010

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

Это означает, что (unsigned int)x >> 33 может делать все, что угодно [1] .

Если левый операнд не подписан, правый сдвиг является логическим сдвигом, поэтому старшие биты будут заполнены нулями.

Это значит 0xFFFFFFFFu >> 4 должно быть 0x0FFFFFFFu

Если левый операнд подписан, правый сдвиг может быть или не быть логическим сдвигом (то есть поведение не определено).

Это означает, что 0xFFFFFFFF >> 4 может быть 0xFFFFFFFF (арифметический сдвиг) или 0x0FFFFFFF (логический сдвиг) или что-либо разрешенное по физическому закону, т.е.

[1]: на 32-разрядной машине с 32-разрядной int.

5 голосов
/ 30 марта 2010

Я предполагаю, что вы знаете, что это значит, переключаясь. Допустим, вы имеете дело с 8-битным char s

unsigned char c;
c >> 9;
c >> 4;
signed char c;
c >> 4;

Первый сдвиг, компилятор может делать все, что захочет, потому что 9> 8 [количество бит в char] Неопределенное поведение означает, что все ставки сняты, нет способа узнать, что произойдет. Вторая смена четко определена. Вы получаете 0s слева: 11111111 становится 00001111. Третья смена, как и первая, не определена.

Обратите внимание, что в этом третьем случае не имеет значения, какое значение c. Когда это относится к signed, это означает тип переменной, а не то, больше или нет фактическое значение, чем ноль. signed char c = 5 и signed char c = -5 оба подписаны, и сдвиг вправо - неопределенное поведение.

2 голосов
/ 30 марта 2010

Чтобы дать некоторый контекст, вот начало этого абзаца:

Операторы сдвига также манипулируют битами. Оператор сдвига влево (<<) создает операнд слева от оператора, сдвинутый влево на число битов, указанное после оператора. Оператор сдвига вправо (>>) создает операнд слева от оператора, сдвинутый вправо на количество битов, указанное после оператора.

Теперь остальное, с объяснениями:

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

Если у вас 32-разрядное целое число и вы пытаетесь сдвинуть бит на 33 бита, это недопустимо и результат не определен. Другими словами, результатом может быть что угодно, или ваша программа может аварийно завершить работу.

Если левый операнд не подписан, правый сдвиг является логическим сдвигом, поэтому старшие биты будут заполнены нулями.

Это говорит о том, что он определен для записи a >> b, когда a является беззнаковым целым. При смещении вправо младшие значащие биты удаляются, другие биты сдвигаются вниз, а старшие значащие биты становятся равными нулю.

Другими словами:

This:    110101000101010 >> 1
becomes: 011010100010101

Если левый операнд подписан, правый сдвиг может быть или не быть логическим сдвигом (то есть поведение не определено).

На самом деле я считаю, что поведение здесь является реализацией, определенной, когда a является отрицательной, и определяется, когда a является положительной, а не неопределенной, как предлагается в цитате. Это означает, что если вы делаете a >> b, когда a является отрицательным целым числом, может произойти много разных вещей. Чтобы увидеть, что вы получаете, вы должны прочитать документацию для вашего компилятора. Обычной реализацией является смещение нулей, если число положительное, и единиц, если число отрицательное, но вы не должны полагаться на это поведение, если хотите написать переносимый код.

2 голосов
/ 30 марта 2010

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

Если вы попытаетесь сместить 32-разрядное целое число на 33, результат будет неопределенным. т. е. это могут быть или не быть все нули.

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

Тип данных без знака будет дополнен нулями при смещении вправо.

так 1100 >> 1 == 0110

Если левый операнд подписан, сдвиг вправо может быть или не быть логический сдвиг (то есть, поведение не определено).

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

http://en.wikipedia.org/wiki/Logical_shift

1 голос
/ 30 марта 2010

Полагаю, ключевое слово "undefined" означает, что в спецификации не сказано, что должно произойти. Большинство компиляторов будут делать что-то разумное в таких случаях, но вы не можете зависеть от поведения в целом. Обычно лучше избегать вызова неопределенного поведения, если в документации для используемого вами компилятора не указано, что он делает в конкретном случае.

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

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

Третий говорит, что если вы сдвигаете int со знаком вправо, не определено, что будет помещено в левые биты.

...