考虑以下代码:
UInt32 val = 1;
UInt32 shift31 = val << 31; // shift31 == 0x80000000
UInt32 shift32 = val << 32; // shift32 == 0x00000001
UInt32 shift33 = val << 33; // shift33 == 0x00000002
UInt32 shift33a = (UInt32)((UInt64)val << 33); // shift33a == 0x00000000
它不会产生警告(关于使用大于 32 的移位),因此它必须是预期的行为。
实际输出到生成程序集的代码(或至少 Reflector 对代码的解释)是
uint val = 1;
uint shift31 = val << 0x1f;
uint shift32 = val;
uint shift33 = val << 1;
uint shift33a = val << 0x21;
IL(再次,使用反射器)是
L_0000: nop
L_0001: ldc.i4.1
L_0002: stloc.0
L_0003: ldloc.0
L_0004: ldc.i4.s 0x1f
L_0006: shl
L_0007: stloc.1
L_0008: ldloc.0
L_0009: stloc.2
L_000a: ldloc.0
L_000b: ldc.i4.1
L_000c: shl
L_000d: stloc.3
L_000e: ldloc.0
L_000f: conv.u8
L_0010: ldc.i4.s 0x21
L_0012: shl
L_0013: conv.u4
L_0014: stloc.s shift33a
我了解发生了什么(在MSDN中有描述);编译代码时,移位 32 位值时仅使用低 5 位...我很好奇为什么会发生这种情况。
(出现的方式shift33a
也让我认为 Reflector 不太对劲,因为他们对 IL 的 c# 表示会编译成不同的东西)
问题:
- 为什么只使用“要移位的值”的低 5 位?
- 如果“移动超过 31 位没有意义”,为什么没有警告?
- 这是向后兼容的事情吗(即这是程序员“期望”发生的事情)?
- 我是否正确,底层 IL 可以进行超过 31 位的移位(如
L_0010: ldc.i4.s 0x21
),但编译器正在修整这些值?