Roy*_*mir 6 .net c# byte short
为什么 :
short a=0;
Console.Write(Marshal.SizeOf(a));
Run Code Online (Sandbox Code Playgroud)
节目 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
Run Code Online (Sandbox Code Playgroud)
第1行的LDC表明:
将0作为int32推入堆栈.
所以必须4占用字节数.
但sizeOf显示2字节...
我在这里错过了什么?短片实际上占用了多少字节?
我听说有一个填充到4个字节的情况,所以处理起来会更快.是这样的吗?
(请忽略syncRoot和GC根标志字节,我只是询问2对4)
CLI规范非常明确地允许在堆栈上的数据类型.短16位整数不是其中之一,因此这些整数类型在加载到堆栈时会转换为32位整数(4个字节).
分区III.1.1包含所有细节:
1.1数据类型
虽然CTS定义了富类型系统,而CLS指定了可用于语言互操作性的子集,但CLI本身处理的是更简单的类型集.这些类型包括用户定义的值类型和内置类型的子集.该子集统称为"基本CLI类型",包含以下类型:
- 全数字类型(的一个子集
int32,int64,native int,和F).- 对象引用(
O)没有区分引用的对象类型.- 指针类型(
native unsigned int和&)没有区分指向的类型.请注意,可以为对象引用和指针类型分配值
null.这在整个CLI中定义为零(所有位为零的位模式).1.1.1数字数据类型
CLI仅对数字类型
int32(4字节有符号整数),int64(8字节有符号整数),native int(本机大小整数)和F(本机大小浮点数)进行操作.但是,CIL指令集允许实现其他数据类型:短整数:评估堆栈仅保存4或8字节整数,但其他位置(参数,局部变量,静态,数组元素,字段)可以保存1或2字节整数.出于堆栈操作的目的,bool和char类型分别被视为无符号1字节和2字节整数.从这些位置加载到堆栈将它们转换为4字节值:
- 对于unsigned int8,unsigned int16,bool和char类型的零扩展;
- 符号扩展为int8和int16类型;
- 零扩展无符号间接和元件的负载(
ldind.u*,ldelem.u*等);; 和- 登录扩展了签名的间接和单元载荷(
ldind.i*,ldelem.i*,等)存储到整数,布尔值,和字符(
stloc,stfld,stind.i1,stelem.i2,等等)截断.使用conv.ovf.*说明检测此截断何时导致值无法正确表示原始值.[注意:短(即1字节和2字节)整数在所有体系结构上作为4字节数加载,并且这些4字节数始终跟踪不同于8字节数.这有助于代码的可移植性,确保默认的算术行为(即,当没有
conv或conv.ovf执行指令时)将在所有实现上具有相同的结果.转换产生短整数值的指令实际上会
int32在堆栈上留下(32位)值,但保证只有低位有意义(即,对于无符号转换,更高有效位全为零或符号扩展为已签名的转换).为了正确地模拟整套转换到短整数之前需要短整数操作div,rem,shr,比较和条件分支指令.
…等等.
在推测性地说,这个决定可能是为了简化建筑或者为了速度(或者两者兼而有之).与使用16位整数相比,现代32位和64位处理器可以更有效地使用32位整数,并且因为所有可以用2个字节表示的整数也可以用4个字节表示,这种行为是合理的.
唯一一次使用2字节整数而不是4字节整数是有意义的,如果你更关心内存使用而不是执行速度/效率.在这种情况下,你需要拥有一大堆这些值,可能包含在一个结构中.那就是你关心的结果Marshal.SizeOf.
通过查看可用的LDC指令,很容易分辨出正在发生的事情.请注意可用的有限操作数类型集,没有可用的类型加载short类型的常量.只是int,long,float和double.这些限制在其他地方可见,例如Opcodes.Add指令同样受限,不支持添加其中一个较小类型的变量.
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
Run Code Online (Sandbox Code Playgroud)
这是IL限制的结果,没有采用字节操作数的add运算符.在这种情况下,它们需要转换为下一个更大的兼容类型int.因此它适用于32位RISC处理器.现在有一个问题,需要将32位的int结果重新打造成一个只能存储8位的变量.C#语言在第一个任务中应用了锤子本身但在第二个任务中不合逻辑地需要一个投掷锤.