根据C ++ 03,5.8 / 2,左移定义如下:
E1的值<&lt; E2是E1(解释为位模式)左移E2位位置;空位是零填充的。如果E1具有无符号类型,则结果的值为E1乘以上升到功率E2的数量2,如果E1的类型为无符号长,则减少模ULONG_MAX + 1,否则为UINT_MAX + 1。
这里困扰我的是明确提到了无符号类型,但完全忽略了已签名的类型。将其与定义右移的5.8 / 3进行比较:
E1的值&gt;&gt; E2是E1右移E2位位置。如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1的商除以提升到功率E2的数量2的积分部分。如果E1具有有符号类型和负值,则结果值是实现定义的。
在5.8 / 3中,明确提到有签名和无签名,即使签署持有非负和签名持有负值也单独提及。
AFAIK在C ++ Standard中未明确定义某些内容时,行为未定义。我也看过this question,但它侧重于C和C ++之间的差异,似乎没有人人都会同意的答案。
左移一个在C ++ 03中定义的有符号整数吗?
答案 0 :(得分:9)
有符号整数使用所谓的2的补码。基本上,如果你将有符号整数移位1,如果它是正数且低于2 ^(位 - 2),它将像无符号一样工作。如果它高于该值但是正数,您将创建一个与原始数字无关的奇怪负数。如果它开头是负面的,你可能会得到负数,可能是正数。
例如,如果我们有一个8位有符号整数,表示-1:
11111111 // -1
如果我们左移,我们最终会
11111110 // -2
但是,假设我们有-120
10001000 // -120
我们最终会以
结束00010000 // 16
显然这不正确!
继续,使用数字65:
01000001 // 65
向左移动,这将成为:
10000001 // -127
等同于-127。
然而,数字16:
00010000 // 16
左移是
00100000 // 32
正如你所看到的,它“有时可行,有时不会”但通常在你的数字低于2 ^(位-2)时有效,有时但如果高于2 ^(位-2 ))。也就是说,向左移动1.向左移动2,再取消一点。等
答案 1 :(得分:9)
我想补充一点,C ++ 11中的规则发生了变化。
在C ++ 11中,负数左侧的带符号左移总是未定义,即使底层机器为范围内的值定义它也是如此。它不是实现定义的,它是 undefined 。这意味着如果你这样做,编译器可以自由地做任何想做的事情,包括意外删除你的一堆代码。这与负数的有符号移位右相反,后者是实现定义的,这意味着其结果取决于机器类型。
Clang的-fsanitize=undefined
模式试图转移左侧负数。