签名右移=奇怪的结果?
我正在帮助某人完成他们的功课,并遇到了这个奇怪的问题。 问题是编写一个函数来反转有符号整数的字节顺序(这就是函数的指定方式),这就是我提出的解决方案:
int reverse(int x) { int reversed = 0; reversed = (x & (0xFF <> 24; reversed |= (x & (0xFF <> 8; reversed |= (x & (0xFF << 8)) << 8; reversed |= (x & 0xFF) << 24; return reversed; }
如果将0xFF000000
传递给此函数,则第一个赋值将导致0xFFFFFFFF
。 我真的不明白发生了什么,但我知道它与签名和未签名之间的来回转换有关,或类似的东西。
如果我将ul
附加到0xFF
它工作正常,我认为这是因为它被强制为无符号然后转换为signed或那个方向的东西。 结果代码也会改变; 没有ul
说明符它使用sar(右移算术),但是作为无符号它使用shr按预期。
如果有人能为我阐明这一点,我将非常感激。 我应该知道这些东西,我以为我做了,但我真的不确定这里发生了什么。
提前致谢!
由于x
是有符号数,因此(x & (0xFF << 24))
是0xFF000000,它也是有符号的,因此自顶部(符号)位置位后为负数 。 int
上的>>
运算符(有符号值)执行符号扩展 (编辑:虽然此行为未定义且特定于实现),并且当值向右移位时传播符号位值1。
您应该按如下方式重写函数,以专门处理无符号值:
unsigned reverse(unsigned x) { unsigned int reversed = 0; reversed = (x & (0xFF << 24)) >> 24; reversed |= (x & (0xFF << 16)) >> 8; reversed |= (x & (0xFF << 8)) << 8; reversed |= (x & 0xFF) << 24; return reversed; }
根据您的结果,我们可以推断出您使用的是32位计算机。
(x & (0xFF << 24)) >> 24
在这个表达式中, 0xFF
是一个int
,所以0xFF << 24
也是一个int
, x
。
当你在两个int
之间执行按位&
,结果也是一个int
,在这种情况下,值是0xFF000000
,在32位机器上意味着符号位被设置,所以你有一个负数。
对具有负值的带符号类型的对象执行右移的结果是实现定义的。 在您的情况下,执行符号保留算术右移。
如果你右移一个无符号类型,那么你将得到你期望的字节反转函数的结果。 您可以通过使按位&
操作数的操作数为无符号类型来强制将两个操作数转换为无符号类型来实现此目的。 (对于任何一个实现的情况都是如此,其中signed int
不能保存unsigned int
所有可能的正值范围,这几乎是所有实现。)
对签名类型的右移是实现定义的,特别是编译器可以随意进行算术或逻辑移位。 如果你所处理的具体价值是正面的,你就不会注意到这一点,但一旦它是负面的,你就可能陷入陷阱。
只是不要这样做,这是不便携的。
x
是有符号的,因此最高位用于符号。 0xFF000000表示“负0x7F000000”。 当您进行移位时,结果为“符号扩展”:左侧添加的二进制数字替换右移的前MSB,始终与值的符号相同。 所以
0xFF000000 >> 1 == 0xFF800000 0xFF000000 >> 2 == 0xFFC00000 0xFF000000 >> 3 == 0xFFE00000 0xFF000000 >> 4 == 0xFFF00000
如果移位的值是无符号的,或者移位是向左移动,则新位将为0.只有在有符号值的右移时,符号扩展才起作用。
如果您希望它在带有有符号和无符号整数的平台上工作相同,请更改
(x & (0xFF << 24)) >> 24
成
(x >> 24) & 0xFF
如果这是java代码,你应该使用’>>>’这是一个无符号右移,否则它将签署扩展值