Оба операнда <<
будут подвергаться целочисленным преобразованиям , т.е. C11 6.3.11p2 :
2 Следующее может использоваться в выражении везде, где могут использоваться int или unsigned int:
- Объект или выражение с целочисленным типом (кроме int или unsigned int), чей целочисленный ранг преобразования меньше или равен рангу int и unsigned int.
- Битовое поле типа _Bool, int, sign int или unsigned int.
Если int
может представлять все значения исходного типа (как ограничено шириной для битового поля), значение преобразуется в int
; в противном случае он преобразуется в unsigned int
.
Поскольку int
на вашей платформе имеет ширину 32 бита, все значения uint16_t
представляются int
. uint32_t
преобразуется в unsigned int
!
Теперь поведение их обоих кажется равным , потому что GCC гарантирует большую часть этого! Вся подписанная арифметика на всех архитектурах, поддерживаемых GCC, использует дополнение 2; и дополнительно GCC не считает поведение <<
для чисел со знаком неопределенным в случаях, когда бит знака изменяется .
Тем не менее, все еще не определено (даже в GCC), что происходит, если ширина сдвига больше или равна ширине операнда (в данном случае 32 бита), поэтому << 32
и << 33
будет иметь неопределенное поведение.
Кроме этого, как правило, стандарт C говорит, что поведение не определено, если целое положительное число со знаком смещено влево, так что бит знака изменяется! Это происходит, когда вы сдвигаете uint16_t
влево на столько бит, что он изменит бит сдвига int
. Следовательно,
(uint16_t)0xFFFF << 16
имеет неопределенное поведение на 32-битной платформе, поскольку самый верхний бит смещен в знаковый бит int
, тогда как
(uint32_t)0xFFFF << 16
нет, потому что последний будет использовать беззнаковую арифметику. Как всегда, компилятор может определить поведение, выходящее за рамки стандартного.