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

时间:2015-05-23 00:16:41

标签: c++ bit-manipulation operators

我试图清楚地理解移位运算符的行为(特别是对于边界情况),所以我设计了一个用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(" ");
     }
   }

3 个答案:

答案 0 :(得分:3)

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

  

E1 << E2的值为E1左移E2位位置;空位是零填充的。如果E1有未签名的   类型,[...]。否则,如果E1具有签名类型和非负值,并且E1×2 E2 可在结果类型的相应无符号类型中表示,然后转换为结果类型的那个值是   结果价值;否则,行为未定义。

uint32_t无法表示任何(奇数> 1)x 2 31 ,因此行为未定义。您的编译器显然选择将其实现为分段错误,这完全符合行为(编辑:呃,至少它会是,如果它正在发生的地方)。

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

答案 1 :(得分:3)

根据你的代码,如果你试图打印负值,由于无限递归,我会在stackoverflow上下注。

   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(2的补码8位中的-4)向右移动1会导致1111 1110(-2的两个如你所见,总是再次填满符号位,这样你的数字就永远不会达到0而if条件总是为真。

通常对有符号值进行位操作是一个坏主意,它们在少数情况下涉及实现/未定义行为。在使用位操作之前,最好将所有值转换为unsigned。

答案 2 :(得分:1)

当我尝试得到-214783648这是整数中已知的最小值...这意味着你要制作一个大于允许范围的整数,这就是为什么在你遇到分段错误的原因。 ..