为什么 :
short a=0;
Console.Write(Marshal.SizeOf(a));
shows 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 表示:
将 0 压入堆栈,如下所示int32.
所以一定有4
占用的字节数。
But sizeOf
shows 2
字节...
我在这里缺少什么? Short 在内存中实际占用多少字节?
我听说过一种情况,填充为 4 个字节,这样处理起来会更快。这里也是这样吗?
(请忽略syncRoot和GC根标志字节,我只是问2 vs 4)
CLI 规范对于允许在堆栈上的数据类型非常明确。短 16 位整数不是其中之一,因此此类类型的整数在加载到堆栈时会转换为 32 位整数(4 个字节)。
第 III.1.1 部分包含所有详细信息:
1.1 数据类型
CTS 定义了丰富的类型系统,CLS 指定了可用于语言的子集
互操作性方面,CLI 本身处理一组简单得多的类型。这些类型包括用户定义的值
类型和内置类型的子集。该子集统称为“基本 CLI 类型”,包含
以下类型:
- 完整数字类型的子集 (
int32
, int64
, native int
, and F
).
- 对象引用(
O
) 不区分引用的对象类型。
- 指针类型(
native unsigned int
and &
) 不区分所指向的类型。
注意对象引用和指针类型都可以赋值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*
, ETC。);;和
- 符号扩展用于有符号间接和元素加载(
ldind.i*
, ldelem.i*
, etc.)
存储为整数、布尔值和字符 (stloc
, stfld
, stind.i1
, stelem.i2
等)截断。使用conv.ovf.*
用于检测此截断何时导致无法正确表示原始值的值的指令。
[注意:短整数(即 1 字节和 2 字节)在所有体系结构上都作为 4 字节数字加载,并且这些 4 字节数字始终与 8 字节数字不同地被跟踪。这通过确保默认的算术行为(即,当没有conv
or conv.ovf
指令被执行)将在所有实现上产生相同的结果。]
产生短整数值的转换指令实际上留下了一个int32
(32 位)值在堆栈上,但保证只有低位有意义(即,对于无符号转换,较高有效位全部为零,对于有符号转换,则为符号扩展)。为了正确模拟完整的短整数运算集,需要在执行之前转换为短整数。div
, rem
, shr
, 比较
和条件分支指令。
…等等。
推测起来,这个决定可能是为了架构简单性或速度(或可能两者兼而有之)。现代 32 位和 64 位处理器处理 32 位整数比处理 16 位整数更有效,并且由于所有可以用 2 字节表示的整数也可以用 4 字节表示,因此这种行为是合理的。
使用 2 字节整数而不是 4 字节整数真正有意义的唯一情况是,如果您更关心内存使用而不是执行速度/效率。在这种情况下,您需要拥有一大堆这些值,可能会打包到一个结构中。那就是你关心结果的时候Marshal.SizeOf
.
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)