short a=0; Console.Write(Marshal.SizeOf(a));
显示2
但如果我看到IL代码,我看到:
/*1*/ IL_0000: ldc.i4.0 /*2*/ IL_0001: stloc.0 /*3*/ IL_0002: ldloc.0 /*4*/ IL_0003: Box System.Int16 /*5*/ IL_0008: call System.Runtime.InteropServices.Marshal.SizeOf /*6*/ IL_000D: call System.Console.Write
第1行的LDC表明:
Push 0 onto the stack as int32.
所以必须占用4个字节.
但sizeOf显示2个字节……
我在这里错过了什么?短片实际上占用了多少字节?
我听说有一个填充到4个字节的情况,所以处理起来会更快.是这样的吗?
(请忽略syncRoot和GC根标志字节,我只是询问2对4)
解决方法
IL指令集是以这种方式有意设计的,它反映了简单的32位处理器的功能.想到的处理器类型是RISC类型,它们在九十年代就有它们的干草日.许多32位cpu寄存器只能操作32位整数和IEEE-754浮点类型. Intel x86内核不是一个很好的例子,虽然非常常用,但它是一种CISC设计,实际上支持在8位和16位操作数上加载和算术运算.但这更像是一次历史性事故,它使程序的机械翻译变得简单,从8位8080和16位8086处理器开始.但是这种功能并不是免费的,操纵16位值实际上需要额外的cpu周期.
使IL与32位处理器功能完美匹配显然使得实现抖动的人的工作变得更加简单.存储位置仍然可以更小,但只需要支持加载,存储和转换.只有在需要时,你的’a’变量才是一个局部变量,无论如何都要占用堆栈帧或cpu寄存器的32位.只有存储到内存需要被截断到正确的大小.
否则代码段中没有歧义.变量值需要加框,因为Marshal.SizeOf()接受了object类型的参数.盒装值通过类型句柄标识值的类型,它将指向System.Int16. Marshal.SizeOf()具有内置的知识,知道它需要2个字节.
这些限制确实反映在C#语言上并导致不一致.这种编译错误永远困扰并惹恼C#程序员:
byte b1 = 127; b1 += 1; // no error b1 = b1 + 1; // error CS0266
这是IL限制的结果,没有采用字节操作数的add运算符.在这种情况下,它们需要转换为下一个更大的兼容类型int.因此它适用于32位RISC处理器.现在有一个问题,需要将32位的int结果重新打造成一个只能存储8位的变量. C#语言在第一个任务中应用了锤子本身但在第二个任务中不合逻辑地需要一个投掷锤.