【发布时间】:2014-10-03 17:11:42
【问题描述】:
这是一个非常简单的 C# 控制台应用程序/Framework 4.5,并使用 Visual Studio 2013 Update 2 编译。它从字节数组中读取双精度,并预先进行简单的范围检查。
class Program
{
private static byte[] fData = new byte[8];
public static unsafe double ReadDouble(int offset)
{
if ((offset + 8) > fData.Length)
return 0;
double value = 0;
fixed (byte* pBuffer = fData)
value = *((double*)(pBuffer));
Console.WriteLine("This code is never reached!");
return value;
}
static void Main(string[] args)
{
fData = BitConverter.GetBytes(1234.56789d);
double result = ReadDouble(0);
Console.WriteLine("result: {0}", result);
Console.ReadKey();
}
}
在没有 /optimize 标志的情况下编译时,任何 CPU 配置都会输出以下内容(并且生成的 IL 似乎有点狡猾):
result: 0
当使用 /optimize 标志编译时,它会正确输出:
This code is never reached!
result: 1234,56789
关于为什么会这样的任何想法?
PS:如果范围检查被注释掉(if((offset ...)),那么它会再次输出正确的结果。
编辑:这是为 ReadDouble() 函数生成的 IL,请注意缺少 Console.WriteLine
.method public hidebysig static
float64 ReadDouble (
int32 offset
) cil managed
{
// Method begins at RVA 0x2060
// Code size 81 (0x51)
.maxstack 2
.locals init (
[0] float64 'value',
[1] bool,
[2] float64,
[3] uint8& pinned pBuffer,
[4] uint8[]
)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.8
IL_0003: add
IL_0004: ldsfld uint8[] WeirdBug.Program::fData
IL_0009: ldlen
IL_000a: conv.i4
IL_000b: cgt
IL_000d: ldc.i4.0
IL_000e: ceq
IL_0010: stloc.1
IL_0011: ldloc.1
IL_0012: brtrue.s IL_0020
IL_0014: ldc.r8 0.0
IL_001d: stloc.2
IL_001e: br.s IL_004f
IL_0020: ldc.r8 0.0
IL_0029: stloc.0
IL_002a: ldsfld uint8[] WeirdBug.Program::fData
IL_002f: dup
IL_0030: stloc.s 4
IL_0032: brfalse.s IL_003a
IL_0034: ldloc.s 4
IL_0036: ldlen
IL_0037: conv.i4
IL_0038: brtrue.s IL_003f
IL_003a: ldc.i4.0
IL_003b: conv.u
IL_003c: stloc.3
IL_003d: br.s IL_0048
IL_003f: ldloc.s 4
IL_0041: ldc.i4.0
IL_0042: ldelema [mscorlib]System.Byte
IL_0047: stloc.3
IL_0048: ldloc.3
IL_0049: conv.i
IL_004a: ldind.r8
IL_004b: stloc.0
IL_004c: ldc.i4.0
IL_004d: conv.u
IL_004e: stloc.3
IL_004f: ldloc.2
IL_0050: ret
}
【问题讨论】:
-
注意
fData = new byte[8]中创建的数组被fData = BitConverter.GetBytes(1234.56789d);扔掉了 -
如果您认为您的 MSIL 不可靠,请展示它....
-
@AdrianoRepetti 十进制的后缀是 m。 double 的后缀是 d。
-
天哪,你是对的。是时候出去散步了……
-
在您的 MSIL 中,有 两个 类型为
double的局部变量,一个是返回值,而value是另一个。你确定这来自同一版本的源代码?