C中的位移位无符号长整型 [英] Bit-shifting unsigned longs in C
问题描述
我在我编写的一段代码中发现了一个错误,并已修复了它,但仍然无法解释发生了什么。归根结底是:
unsigned i = 1<<31; // gives 21476483648 as expected
unsigned long l = 1<<31; // gives 18446744071562067968 as not expected
我在这里注意到一个问题:Unsigned long and bit shifting其中完全相同的数字显示为意外的值,但在那里他使用的是有符号的字符,我相信这会导致符号扩展。我真的无论如何都不明白为什么我在这里得到了一个不正确的值。
我在Ubuntu 18.04上使用Clion,在我的系统上,aunsigned
是32位,along
是64位。
推荐答案
在此表达式中:
1<<31
值1
具有类型int
。假设int
是32位宽,这意味着您要将一位移入符号位。这样做是undefined behavior。
C standard的6.5.7p4节记录了这一点:
e1;<;e2的结果是e1左移e2位位置;空出 位由零填充。如果e1具有无符号类型,则 结果的值是E1×2E2,模1比 结果类型中可表示的最大值。如果e1具有 带符号类型和非负值,以及E1×2E2可在 结果类型,则这是结果值;否则, 行为未定义。
但是,因为您使用的是使用GCC的Ubuntu,所以行为实际上是由实现定义的。gcc documentation说明:
按位运算符作用于值的表示形式,包括 符号位和值位,其中考虑符号位 紧接在最高值位的上方。签名>>
作用于 带符号扩展的负数。 作为C语言的扩展,GCC没有使用给定的纬度 在C99和C11中,仅将签名的<<
的某些方面视为 未定义。但是,-fsanitize=shift
(和-fsanitize=undefined
)将 诊断这类病例。他们也被诊断为常量 需要表达式。
所以在这种情况下,GCC直接致力于值的表示。这意味着1<<31
具有类型int
和表示0x80000000
。此表示法的十进制表示值为-2147483648。
将此值分配给unsigned int
时,将通过6.3.1.3p2:
否则,如果新类型是无符号的,则值为 通过重复添加或减去 可以用新类型表示的最大值,直到 值在新类型的范围内。
由于32位unsigned int
的"One More the Maximum Value"为42949672956,因此将int
值-2147483648转换为unsigned int
值42949672956-2147483648==2147483648。
将1<<31
分配给64位的unsigned long int
时,"比最大值多1"为18446744073709551616,因此转换结果为18446744073709551616-2147483648==18446744071562067968,这是您获得的值。
若要获取正确的值,请使用UL
后缀将值unsigned long
:
1UL<<31
这篇关于C中的位移位无符号长整型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!