如果移位运算符后的值 大于的位数 左手操作数,结果是 未定义。如果左边的操作数是 无符号,右移是合乎逻辑的 移位所以高位将被填充 用零。如果是左手操作数 已经签署,正确的转变可能或可能 不是一个逻辑转变(即, 行为未定义)。
有人能解释一下上面这些行是什么意思吗?
答案 0 :(得分:23)
这些线的含义并不重要,它们实际上是不正确的。
“如果移位运算符后的值 大于的位数 左手操作数,结果是 未定义“。
是真的,但应该说“大于或等于”。 5.8 / 1:
...如果是,行为是不确定的 右手操作数是否定的,或者 大于或等于的长度 升级左操作数的位。
未定义的行为意味着“不要这样做”(见后文)。也就是说,如果您的系统上int
为32位,那么您无法有效地执行以下任何操作:
int a = 0; // this is OK
a >> 32; // undefined behavior
a >> -1; // UB
a << 32; // UB
a = (0 << 32); // Either UB, or possibly an ill-formed program. I'm not sure.
“如果左侧操作数是无符号的,则右移是逻辑移位,因此高位将用零填充。”
这是事实。 5.8 / 3说:
如果E1具有无符号类型或E1具有 签名类型和非负值, 结果是整体的一部分 E1的商除以数量 2提升到权力E2
如果这对你更有意义。 >>1
与除以2相同,>>2
除以4,>>3
除以8,依此类推。在正值的二进制表示中,除以2与向右移动所有位,丢弃最小位,并用0填充最大位相同。
“如果左侧操作数已签名,则右移可能是也可能不是逻辑移位(即行为未定义)。”
第一部分是正确的(它可能是也可能不是逻辑转变 - 它在某些编译器/平台上但不在其他编译器/平台上。我认为到目前为止最常见的行为是它不是)。第二部分是假的,行为不是未定义的。未定义的行为意味着允许任何事情发生 - 崩溃,恶魔飞出你的鼻子,随机值,等等。标准不关心。有很多情况下C ++标准表明行为是未定义的,但这不是其中之一。
实际上,如果左手操作数是有符号的,并且值是正的,那么它的行为与无符号的移位相同。
如果左手操作数已签名,且值为负,则结果值是实现定义的。它不允许坠毁或着火。实现必须产生结果,并且实现的文档必须包含足够的信息来定义结果。实际上,“实现文档”从编译器文档开始,但这可能会隐式或显式地引用您的操作系统和/或CPU的其他文档。
再次从标准,5.8 / 3:
如果
E1
已签署类型且为负数 值,结果值是 实现定义的。
答案 1 :(得分:5)
我假设你知道改变意味着什么。假设您正在处理8位char
s
unsigned char c;
c >> 9;
c >> 4;
signed char c;
c >> 4;
第一个转变,编译器可以自由地做任何想做的事,因为9&gt; 8 [char
]中的位数。未定义的行为意味着所有赌注都已关闭,无法知道将会发生什么。第二个转变很明确。您在左侧获得0:11111111
变为00001111
。第三个转变与第一个转变一样,未定义。
请注意,在第三种情况下,c
的值无关紧要。当它引用signed
时,它表示变量的类型,而不是实际值是否大于零。 signed char c = 5
和signed char c = -5
都已签名,向右移动是未定义的行为。
答案 2 :(得分:5)
如果移位运算符之后的值大于左侧操作数中的位数,则结果是未定义的。
这意味着(unsigned int)x >> 33
can do anything [1] 。
如果左侧操作数是无符号的,则右移是逻辑移位,因此高位将用零填充。
这意味着0xFFFFFFFFu >> 4
必须是0x0FFFFFFFu
如果左侧操作数已签名,则右移可能是也可能不是逻辑移位(即行为未定义)。
这意味着0xFFFFFFFF >> 4
可以是0xFFFFFFFF
(算术移位)或0x0FFFFFFF
(逻辑移位)或任何物理法允许的,即结果未定义。
[1]:在具有32位int
的32位机器上。
答案 3 :(得分:2)
如果移位运算符后的值 大于的位数 左手操作数,结果是 未定义。
如果您尝试将32位整数移位33,则结果未定义。即,它可能全部为零,也可能不是全零。
如果左侧操作数未签名, 正确的转变是合乎逻辑的转变 高位将填充 零。
右移时,无符号数据类型将用零填充。
所以1100 >> 1 == 0110
如果左手操作数已签名, 正确的转变可能是也可能不是 逻辑转变(即行为 未定义)。
如果数据类型已签名,则不会定义行为。签名数据类型以特殊格式存储,其中最左边的位表示正或负。因此,对有符号整数进行移位可能无法达到预期效果。有关详细信息,请参阅Wikipedia文章。
答案 4 :(得分:2)
为了给出一些背景,这是该段的开头:
移位运算符也操纵位。左移位运算符(&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;右移运算符(&gt;&gt;)产生运算符左侧的操作数,向右移动运算符后指定的位数。
现在剩下的就是解释:
如果移位运算符之后的值大于左侧操作数中的位数,则结果是未定义的。
如果你有一个32位整数,你试图将33位移位,那是不允许的,结果是未定义的。换句话说,结果可能是任何结果,或者您的程序可能崩溃。
如果左侧操作数是无符号的,则右移是逻辑移位,因此高位将用零填充。
这表示当a是unsigned int时,它被定义为写a >> b
。向右移位时,最低有效位被移除,其他位向下移位,最高有效位变为零。
换句话说:
This: 110101000101010 >> 1 becomes: 011010100010101
如果左侧操作数已签名,则右移可能是也可能不是逻辑移位(即行为未定义)。
实际上我认为这里的行为是在a
为负时定义的实现,并且当a
为正时定义,而不是如引用中所建议的那样未定义。这意味着如果在a >> b
为负整数时执行a
,则可能会发生许多不同的事情。要查看您获得的内容,您应该阅读编译器的文档。一个常见的实现是如果数字为正数则为零,如果数字为负数则为1,但如果您希望编写可移植代码,则不应依赖此行为。
答案 5 :(得分:1)
我认为关键词是“未定义”,这意味着规范没有说明应该发生什么。大多数编译器会在这种情况下做一些合理的事情,但你不能完全依赖任何行为。通常最好避免调用未定义的行为,除非您使用的编译器的文档说明了它在特定情况下的作用。
如果您尝试将32位值移位超过32位,则第一句话表示未定义。
第二个说如果右移一个unsigned int,左边的位将被零填充。
第三个说如果你正确地移动一个有符号的int,那么就没有定义将放在左手位中的内容。