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