在《C# 5.0权威指南》一书的子主题存储开销中,有一条通用注释如下:
byte
字段占用1个字节,而每个long
字段占用8个字节。这意味着,虽然b
可以放置在内存中的任何位置,但l
需要放置在地址为8的倍数的位置。它不能放置在地址0
,因为那里已经被b
占用;因此,它必须放置在下一个可用的8的倍数的地址上,即8
,导致7个字节的空间浪费。---------------------------------------------------------------------------------
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
---------------------------------------------------------------------------------
<--b-> <------------------l-------------------->
<--------------waste------------->
请注意对齐方式。 Long类型必须位于位置0、8、16等位置...
但是如果我们先看字节,则如下所示:
b-------llllllll
假设b是一个字节,l是一个长整型。减号代表“浪费的空间”。 因此,可以看出这个结构体使用了整整16个字节,但只使用了9个字节,因此有7个字节被浪费了。
这个结构体默认的布局方式有两个原因:
1) 性能,正如其他人已经解释的那样。将字段成员放置在它们大小的倍数位置可以实现更快的传输。
2) 与大多数C/C++编译器的默认设置相同。这使得与C/C++进行交互(包括所有Windows API)变得更加容易。
请注意,如果您不需要转换结构体,则可以使用[StructLayout(LayoutKind.Auto)]
:
[StructLayout(LayoutKind.Auto)]
struct A
{
byte b;
long l;
}
有了这个,接下来的代码如下
unsafe
{
Console.WriteLine(sizeof(A));
}
输出12
,表示打包效果更好。
如果您使用 [StructLayout(LayoutKind.Sequential, Pack = 1)]
,则可以将大小缩小到9
。
它们通常与处理器字边界对齐,以便检索它们是一个简单的单周期操作。否则,CLR必须获取整个地址,并XOR / shift结构字段以引用它。