int
— целочисленный тип со знаком. C11 6.5.7p4-5 говорит следующее:
4 Результатом E1 << E2
является E1
сдвиг влево на E2
битовых позиций; освободившиеся биты заполняются нулями. [...] Если E1
имеет тип со знаком и неотрицательное значение, а E1 x 2E2 может быть представлено в типе результата, то это результирующее значение; в противном случае поведение не определено.
5 Результатом E1 >> E2
является E1
сдвинутых вправо E2
битовых позиций. [...] если E1
имеет тип со знаком и неотрицательное значение, значение результата является неотъемлемой частью частного E1 / 2E2 . Если E1
имеет тип со знаком и отрицательное значение, результирующее значение определяется реализацией.
Таким образом, в случае <<
, если сдвинутое значение отрицательно или положительное значение после сдвига не представляется в типе результата (здесь: int
), поведение не определено; в случае >>
, если значение отрицательное, результатом является определенная реализация.
Таким образом, в любом случае вы получите результаты, которые, по крайней мере, зависят от реализации, а в случае сдвига влево, что еще хуже, возможно, от уровня оптимизации и тому подобного. Строго соответствующая программа не может полагаться на какое-либо конкретное поведение.
Однако, если вы хотите настроить таргетинг на конкретный компилятор, проверьте в его руководствах, каким будет поведение, если оно указано. Например, GCC говорит :
Результаты некоторых побитовых операций над целыми числами со знаком (C90 6.3, C99 и C11 6.5).
Побитовые операторы работают с представлением значения, включая биты знака и значения, где бит знака считается непосредственно над битом самого высокого значения. Знак ">>" действует на отрицательные числа по расширению знака. [*]
Как расширение языка C, GCC не использует широту, указанную в C99 и C11, только для обработки определенных аспектов подписанного '‹‹' как неопределенного. Однако -fsanitize=shift (и -fsanitize =undefined) будет диагностировать такие случаи. Они также диагностируются там, где требуются постоянные выражения.
[*] расширение знака здесь означает, что бит знака, равный 1
для отрицательных целых чисел, повторяется на величину сдвига при выполнении сдвига вправо - вот почему вы видите эти F
в результате.
Кроме того, GCC всегда требует представления дополнения до 2, поэтому, если бы вы всегда использовали GCC, независимо от того, на какую архитектуру вы ориентируетесь, это поведение, которое вы увидите. Кроме того, в будущем кто-то может использовать другой компилятор для вашего кода, что приведет к другому поведению.
Возможно, вы захотите использовать беззнаковые целые числа - unsigned int
или, скорее, если ожидается определенная ширина, то, например, uint32_t
, так как сдвиги всегда четко определены для нее и, казалось бы, соответствуют вашим ожиданиям.
Еще одна вещь, которую следует отметить, это то, что не все суммы смен разрешены. C11 6.5.7 p3:
[...] Если значение правого операнда отрицательно или больше или равно ширине расширенного левого операнда, поведение не определено.
Таким образом, если вы когда-нибудь сдвинете целое число без знака шириной 32 бита на 32 — влево или вправо, поведение будет неопределенным. Это следует иметь в виду. Даже если бы компилятор не сделал ничего странного, некоторые процессорные архитектуры действуют так, как если бы смещение на 32 сдвинуло бы все биты, а другие ведут себя так, как если бы величина сдвига была равна 0.
person
Antti Haapala
schedule
01.07.2017
0x8F5AEB9C
вint
на своей платформе? - person WhozCraig   schedule 01.07.2017