为什么将变量移位超过其位宽度会归零?

Gal*_*axy 3 c bit-shift undefined-behavior

这个问题的灵感来自 StackOverflow 的其他问题。今天,在浏览 StackOverflow 时,我遇到了一个问题,即将变量按值 k 进行位移,该值 >= 该变量的位宽。这意味着将 32 位 int 移位 32 位或更多位。

将整数左移 32 位

意外的 C/C++ 按位移位运算符结果

从这些问题中可以明显看出,如果我们尝试将数字移位 >= 变量位宽的 k 位,则仅采用最低有效 log2k 位。对于 32 位 int,最低有效 5 位被屏蔽并作为移位量。

因此,一般来说,如果 w = 变量的位宽, x >> k则变为x >> (k % w) 对于 an int,则为x >> (k % 32)

计数被屏蔽为 5 位,这将计数范围限制为 0 到 31。

所以我编写了一个小程序来观察理论上应该产生的行为。我在评论中写下了最终的偏移量 % 32。

#include <stdio.h>
#include <stdlib.h>

#define PRINT_INT_HEX(x) printf("%s\t%#.8x\n", #x, x);

int main(void)
{
    printf("==============================\n");
    printf("Testing x << k, x >> k, where k >= w\n");

    int      lval = 0xFEDCBA98 << 32;
    //int      lval = 0xFEDCBA98 << 0;

    int      aval = 0xFEDCBA89 >> 36;
    //int      aval = 0xFEDCBA89 >> 4;

    unsigned uval = 0xFEDCBA89 >> 40;
    //unsigned uval = 0xFEDCBA89 >> 8;

    PRINT_INT_HEX(lval)
    PRINT_INT_HEX(aval)
    PRINT_INT_HEX(uval)

    putchar('\n');

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

并且输出与移位指令的预期行为不匹配!

==============================
Testing x << k, x >> k, where k >= w
lval    00000000 
aval    00000000
uval    00000000
Run Code Online (Sandbox Code Playgroud)

=================================================== ===================

其实我对Java有点困惑。在 C/C++ 中,将 int 移位大于位宽的位数可能会减少 k % w,但 C 标准不保证这一点。没有任何规则规定这种行为应该一直发生。这是未定义的行为。

然而,Java 中就是这种情况。这是Java编程语言的规则。

Java语言规范中的位移运算符描述

Java整数左移的奇怪结果

java:int 的移位距离限制为 31 位

dbu*_*ush 6

链接的问题特别指出,移位量大于要移位的类型的位宽会引发未定义的行为,该标准将其定义为“在使用不可移植或错误的程序构造或错误数据时的行为,对此国际标准没有提出任何要求”

当您调用未定义的行为时,任何事情都可能发生。程序可能会崩溃,可能会输出奇怪的结果,或者可能看起来工作正常。此外,如果您在同一编译器上使用不同的编译器或不同的优化设置,则未定义行为的表现方式可能会发生变化。

C 标准在第 6.5.7p3 节中对按位移位运算符进行了以下规定:

对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。 如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

在这种情况下,编译器可能会按照您的建议减少以位宽为模的移位量,或者可以将其视为按该量进行数学移位,导致所有位均为 0。这两种结果都是有效的,因为标准不这样做不指定行为。