C shift right 在我的程序中对 int 类型无法正常工作

C shift right not working correctly on int types in my program

我在C中有如下函数:

int lrot32(int a, int n)
{
    printf("%X SHR %d = %X\n",a, 32-n, (a >> (32-n)));
    return ((a << n) | (a >> (32-n)));
}

当我将 lrot32(0x8F5AEB9C, 0xB) 作为参数传递时,我得到以下信息:

8F5AEB9C shr 21 = FFFFFC7A

但是,结果应该是47A。我做错了什么?

感谢您的宝贵时间

int 是有符号整数类型。 C11 6.5.7p4-5 表示如下:

4 The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. [...] If E1 has a signed type and nonnegative value, and E1 x 2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

5 The result of E1 >> E2 is E1 right-shifted E2 bit positions. [...] if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined.

因此在<<的情况下,如果移位后的值为负,或者移位后的正值在结果类型中不可表示(此处:int),则行为未定义;在 >> 的情况下,如果值为负,则结果为 实现定义

因此,在任何一种情况下,您都会得到至少取决于实现的结果,而在左移的情况下,更糟的是,可能取决于优化级别等。一个严格遵守的程序不能依赖于任何特定的行为。


但是,如果您想针对特定的编译器,请查看其手册以了解行为(如果有指定)是什么。例如 GCC says:

The results of some bitwise operations on signed integers (C90 6.3, C99 and C11 6.5).

Bitwise operators act on the representation of the value including both the sign and value bits, where the sign bit is considered immediately above the highest-value value bit. Signed ‘>>’ acts on negative numbers by sign extension. [*]

As an extension to the C language, GCC does not use the latitude given in C99 and C11 only to treat certain aspects of signed ‘<<’ as undefined. However, -fsanitize=shift (and -fsanitize=undefined) will diagnose such cases. They are also diagnosed where constant expressions are required.

[*] 符号扩展在这里意味着符号位 - 对于负整数是 1,在执行右移时被移位量重复 - 这就是为什么你看到那些 Fs 在结果中。

此外,GCC 始终需要 2 的补码表示,因此如果您总是 使用 GCC,无论您的目标是哪种架构,您都会看到这种行为。此外,将来有人可能会为您的代码使用其他编译器,从而导致其他行为。


也许你想使用无符号整数 - unsigned int 或者更确切地说,如果需要一定的宽度,那么例如 uint32_t,因为移位总是明确定义的,并且似乎符合您的期望。


另一件需要注意的事情是,并不是所有的偏移量都是允许的。 C11 6.5.7 p3:

[...]If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

因此,如果您将宽度为 32 位的无符号整数左移或右移 32 位,则行为是 未定义。应该记住这一点。即使编译器不会做任何古怪的事情,一些处理器架构确实表现得好像移位 32 然后将所有位移开 - 其他人表现得好像移位量为 0.