需要一些帮助 - 左移按位运算符

Need some help - left shift bitwise operator

我试图清楚地理解移位运算符的行为(尤其是对于边界情况),所以我设计了一个用 C++ 编写的小测试。

    int a = odd_value; //321 in my case but it should not matter (the last bit to be 1)
    print(0, a);

    a = a << 31; // (this gives a segmentation fault, as normal because it tries the sign bit becomes 1 but all the other bits are 0).
    print(0, a); //the segmentation fault happens here - it prints the minimum integer value and then gives the fault
    a = (a << 1) + 1; // but if then I do this, shouldn't it set a to -1 ??
    print(a); //gives 0

   void print(int stackCallIndex, int nb)
   {
     if(nb)
     {
        print(++stackCallIndex, nb >> 1);
        if(nb & 1) printf("%d", 1);
        else       printf("%d", 0);

        if(stackCallIndex % 8 == 0) printf(" ");
     }
   }

如果您想要标准行为,那么它是未定义的。根据 [expr.shift]:

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, [...]. Otherwise, if E1 has a signed type and non-negative value, and E1×2E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.

Any (odd number > 1) x 231 不能用 uint32_t 表示,因此行为未定义。您的编译器显然选择将此实现为分段错误,这是完全符合要求的行为(编辑:呃,至少它会是,如果那是它发生的地方)。

一种更典型的方法是让位 "fall off" 结束。也就是说,奇数的 a << 31 将变为 0x80000000。但即使在那种情况下,1 的另一个左移将导致 0,因此您必须减去 1 才能得到 -1,而不是添加 1

当我尝试得到 -214783648 时,它是整数中已知的最小值...这意味着您生成的整数大于允许的范围,这就是为什么在您的情况下会出现分段错误...

根据您的代码,如果您尝试打印负值,我敢打赌由于无限递归会导致 Whosebug。

   void print(int stackCallIndex, int nb)
   {
     if(number)
     {
        print(++stackCallIndex, nb >> 1); // likely an infinite recursion here.
        if(nb & 1) printf("%d", 1);
        else       printf("%d", 0);

        if(stackCallIndex % 8 == 0) printf(" ");
     }
   }

那为什么会是无限递归呢?严格按照标准右移负符号整数是实现定义的。

在大多数实现中,它会执行 arithmetic right shift,这意味着 1111 1100(二进制补码 8 位中的 -4)向右移动 1 将导致 1111 1110(-2在二进制补码 8 位中)正如您所看到的,您总是再次填充符号位,因此您的数字永远不会达到 0,并且 if 条件始终为真。


通常对有符号值进行位操作不是一个好主意,它们在少数情况下涉及 implementation/undefined 行为。在使用位操作之前最好将所有值都转换为无符号。