4

我正在帮助某人完成家庭作业并遇到了这个奇怪的问题。问题是编写一个函数来反转有符号整数的字节顺序(这就是函数的指定方式),这是我想出的解决方案:

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. 我真的不明白发生了什么,但我知道这与有符号和无符号之间的来回转换或类似的事情有关。

如果我追加ul0xFF它工作正常,我认为这是因为它被迫无符号然后转换为有符号或那个方向的东西。生成的代码也会发生变化;没有说明ul符,它使用 sar(右移算术),但作为无符号,它按预期使用 shr。

如果有人能为我阐明这一点,我将不胜感激。我应该知道这些东西,我以为我知道了,但我真的不确定这里发生了什么。

提前致谢!

4

6 回答 6

12

由于x是有符号数量,因此(x & (0xFF << 24))0xFF000000 的结果也是有符号的,因此由于设置了最高(符号)位,因此为负数。(有符号值)上的>>运算符执行符号扩展(编辑:尽管此行为未定义且特定于实现)并在值向右移动时传播符号位值 1。int

您应该按如下方式重写函数以专门处理无符号值:

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;
}
于 2010-09-21T18:30:42.993 回答
7

根据您的结果,我们可以推断您使用的是 32 位机器。

(x & (0xFF << 24)) >> 24

在这个表达式0xFF中 is an int, so0xFF << 24也是 an int, as is x

&当您在 two 之间执行按位运算时int,结果也是 an int,在这种情况下,值是0xFF000000which 在 32 位机器上意味着设置了符号位,因此您有一个负数。

对带负值的带符号类型的对象执行右移的结果是实现定义的。在您的情况下,执行保留符号的算术右移。

如果你右移一个无符号类型,那么你会得到你期望的字节反转函数的结果。您可以通过将按位&操作数的任一操作数设为无符号类型来强制将两个操作数转换为无符号类型来实现此目的。(对于任何有符号不能保存几乎所有实现int的正值的所有可能范围的实现都是如此。)unsigned int

于 2010-09-21T18:38:01.687 回答
3

有符号类型的右移是实现定义的,特别是编译器可以随意进行算术或逻辑移位。如果您正在处理的具体值是正面的,您不会注意到这一点,但一旦它是负面的,您可能会掉入陷阱。

只是不要这样做,这不是便携式的。

于 2010-09-21T18:39:15.757 回答
1

x是有符号的,所以最高位用于符号。0xFF000000 表示“负 0x7F000000”。当您进行移位时,结果是“符号扩展”:在左侧添加的二进制数字以替换右移的前 MSB,始终与值的符号相同。所以

0xFF000000 >> 1 == 0xFF800000
0xFF000000 >> 2 == 0xFFC00000
0xFF000000 >> 3 == 0xFFE00000
0xFF000000 >> 4 == 0xFFF00000

如果要移位的值是无符号的,或者向左移位,则新位将为 0。符号扩展仅在有符号值的右移中起作用。

于 2010-09-21T18:33:48.317 回答
1

如果您希望它在具有有符号和无符号整数的 al 平台上同样工作,请更改

(x & (0xFF << 24)) >> 24

进入

(x >> 24) & 0xFF
于 2010-10-06T20:54:58.067 回答
0

如果这是java代码,你应该使用'>>>',这是一个无符号右移,否则它将符号扩展值

于 2010-09-21T19:02:14.573 回答