在C& C ++,如果在使用>>
和<<
(右移和左移运算符)时右操作数为负,则程序的行为未定义。
请考虑以下计划:
#include <iostream>
int main()
{
int s(9);
std::cout<<(s<<-3);
}
g ++提供以下警告:
[Warning] left shift count is negative [enabled by default]
MSVS 2010发出以下警告:
warning c4293: '<<' : shift count negative or too big, undefined behavior
现在我很想知道Java和C#会发生什么?
我尝试过以下程序
class left_shift_nagative
{
public static void main(String args[])
{
int a=3;
System.out.println(a<<-3);
System.out.println(a>>-3);
}
}
计划结果:
1610612736
0
C#轮到:
namespace left_shift_nagative
{
class Program
{
static void Main(string[] args)
{
int s = 3;
Console.WriteLine(s << -3);
Console.WriteLine(s >> -3);
}
}
}
输出:
1610612736
0
1610612736的输出如何?这里发生了什么? Java语言规范(JLS)和C#语言规范或标准对此有何看法?怎么&lt;&lt;和&gt;&gt;当Java和Java中给出负移位计数时,运算符工作。 C#?使用右移时如何输出0?我真的很困惑。
答案 0 :(得分:6)
我将回答有关Java部分的内容(不能代表C#,但它可能是相同的)。
移位运算符>>
和<<
在JLS section 15.19中定义。引用(强调我的):
如果左侧操作数的提升类型是int,则只使用右侧操作数的五个最低位作为移位距离。就好像右手操作数受到按位逻辑AND运算符&amp; (§15.22.1),掩码值为0x1f(0b11111)。 实际使用的移位距离因此始终在0到31的范围内,包括。
因此,当你移动-3
时,就好像你正在移动-3 & 0x1f
,即29(只使用右手操作数的五个最低位) )。
a << -3
的结果是2^29 * a
;对于a = 3
,这是1610612736
。a >> -3
的结果是floor(a / 2^29)
;对于a = 3
,这是0
。请注意,当左操作数的提升类型为long
而不是int
时,使用的掩码值为0x3f
(仅右侧的六个最低位)使用了 - 手操作数。)
答案 1 :(得分:1)
来自C#规范第7.9节 -
对于预定义的运算符,要移位的位数计算如下:
- 当
计算的x
的类型为int
或uint
时,移位计数由count
的低位5位给出。换句话说,移位计数是从count & 0x1F
- 当
计算的x
的类型为long
或ulong
时,移位计数由count
的低位6位给出。换句话说,移位计数是从count & 0x3F
这意味着忽略了班次的标志。事实上正如Jon Skeet在注释规范中指出的那样,由于规范定义的位掩码,以下代码将在i == 31
之后换行。
for (int i = 0; i < 40; i++)
{
Console.WriteLine(int.MaxValue >> i);
}