【发布时间】:2009-03-13 21:37:04
【问题描述】:
考虑以下代码:
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)但编译器正在修整这些值?
【问题讨论】:
-
甚至规范都没有提供关于“为什么”的见解。您可以尝试询问 Eric Lippert 等 C# 编译器专家。
-
@Johannes:是的,我只是想知道是否有人有一个用例或某些东西,为什么这样做是有意义的:-/
-
你永远不应该相信 Reflector。我花了好几个小时来寻找错误,因为我相信输出。 IL 通常是最可靠的,但我也遇到过一些不太顺利的情况。改用 ILDASM。
-
@leppie:是的,这就是我发布 IL 的原因:-]