签名右移=奇怪的结果?

ide*_*ity 4 c endianness undefined-behavior sign-extension

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

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;
}
Run Code Online (Sandbox Code Playgroud)

如果传递0xFF000000给此函数,将导致第一个赋值0xFFFFFFFF.我真的不明白发生了什么,但我知道它与签名和未签名之间的来回转换有关,或类似的东西.

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

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

提前致谢!

Ric*_*ook 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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我希望评论能够被低估.比特31-24交换7-0(轮班24),23-16交换15-8(轮班8,而不是16).回滚到正确的算法. (5认同)
  • 值得一提的是,溢出到符号位的左移具有**未定义的行为**,随后的右移(带符号扩展)是**特定于**的行为.长话短说,**你应该在这里使用无符号类型**. (4认同)

CB *_*ley 7

根据您的结果,我们可以推断出您使用的是32位计算机.

(x & (0xFF << 24)) >> 24
Run Code Online (Sandbox Code Playgroud)

在这个表达式中0xFF是一个int,所以0xFF << 24也是一个int,因为它是x.

当您&在两个之间执行按位时int,结果也是一个int,在这种情况下,该值是0xFF00000032位机器上的值意味着符号位已设置,因此您有一个负数.

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

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