根据C03,5.8/2,左移定义如下:
E1〈〈E2的值是E1(被解释为比特模式)左移E2比特位置;如果E1为无符号类型,则结果值为E1乘以数量2的E2次幂,如果E1为无符号长型,则以ULONG_MAX+1为模,否则以UINT_MAX+1为模。
这里让我困扰的是无符号类型被显式地提到了,而有符号类型却被完全忽略了,这与5.8/3中定义的右移比较一下:
E1〉〉E2的值是E1右移E2位的位置。如果E1为无符号类型或E1为有符号类型且为非负值,则结果值为E1除以数量2的E2次幂所得商的整数部分。如果E1为有符号类型且为负值,则结果值由实现定义。
在5.8/3中,有符号和无符号都被明确提及,甚至有符号的非负值和有符号的负值也被分别提及。
AFAIK当一些东西没有在C标准中显式定义时,行为是未定义的。我也看过this question,但它关注的是C和C之间的差异,似乎没有一个每个人都会同意的答案。
左移是C03中定义的有符号整数吗?
2条答案
按热度按时间sdnqo3pr1#
5.8/2表示它将其解释为位模式,这仅在由于某种原因您的实现没有使用2的补码,或者您的编译器猜测您(他们没有)时才与实现相关。C++11更明确,但表示相同的事情。
有符号整数使用的是2的补码。基本上,如果你将一个有符号整数移位1位,如果它是正的并且小于2^(位-2),那么它就像是一个无符号整数一样工作;如果它大于2 ^(位-2),但是是正的,那么你会创建一个奇怪的负数,它与原始数没有关系;如果它一开始是负的,那么你可能会得到一个负数,也可能是正数。
例如,如果我们有一个8位有符号整数表示-1:
如果我们向左移动,我们得到
但是,假设我们有-120
我们最终会得到
显然这是不正确的!
继续,使用第65号:
左移,将变为:
等于-127。
然而,数字16:
左移为
正如你所看到的,它“有时有效,有时无效”,但通常在你的数字小于2^(bits-2)时有效,在大于-(2^(bits-2))时有时有效,也就是说,左移1位,左移2位,去掉另一位,等等。
s2j5cfk02#
我想补充的是规则在C++11中发生了变化。
在C++11中,带符号的负数左移是 * 总是 * 未定义 * 行为*,即使底层机器为范围内的值定义了它。它不是 * 实现定义的 *,它是 * 未定义的 *。这意味着如果你这样做,编译器可以自由地做任何它想做的事情。包括意外删除一堆代码,这与负数的带符号右移相反,后者是 * 实现定义的 *,意味着它的结果取决于机器类型。
Clang的
-fsanitize=undefined
模式捕获向左移负数的尝试。