我试图清楚地理解移位运算符的行为(特别是对于边界情况),所以我设计了一个用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(" ");
}
}
答案 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这是整数中已知的最小值...这意味着你要制作一个大于允许范围的整数,这就是为什么在你遇到分段错误的原因。 ..