什么是布尔在C#中的大小? 真的需要4个字节吗?

我有两个字节和布尔值数组的结构:

using System.Runtime.InteropServices; [StructLayout(LayoutKind.Sequential, Pack = 4)] struct struct1 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public byte[] values; } [StructLayout(LayoutKind.Sequential, Pack = 4)] struct struct2 { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public bool[] values; } 

和下面的代码:

 class main { public static void Main() { Console.WriteLine("sizeof array of bytes: "+Marshal.SizeOf(typeof(struct1))); Console.WriteLine("sizeof array of bools: " + Marshal.SizeOf(typeof(struct2))); Console.ReadKey(); } } 

这给了我以下输出:

 sizeof array of bytes: 3 sizeof array of bools: 12 

这似乎是一个boolean需要4个字节的存储空间。 理想情况下, boolean只需要一位( falsetrue01等)。

这里发生了什么? booleantypes真的非常低效吗?

布尔types有一个方格的历史与语言运行时之间的许多不兼容的select。 这是从发明C语言的人Dennis Ritchie的历史devise开始的。 它没有booltypes,替代方法是int ,其中值为0表示为false ,任何其他值均为

这个select是在Winapi中进行的,这是使用pinvoke的主要原因,它有一个BOOL的typedef,它是C编译器的int关键字的别名。 如果不应用明确的[MarshalAs]属性,那么C# 布尔转换为BOOL,从而产生一个4字节长的字段。

无论你做什么,你的结构声明都需要与你所使用的语言的运行时select相匹配。 如前所述,BOOL为winapi,但大多数C ++实现select字节 ,大多数COM自动化interop使用VARIANT_BOOL这是一个简短的

一个C# bool实际大小是一个字节。 CLR的一个强大的devise目标是你无法find答案。 布局是一个取决于处理器太多的实现细节。 处理器对variablestypes和alignment方式非常挑剔,错误的select会显着影响性能并导致运行时错误。 通过使布局不可被发现,.NET可以提供一个不依赖于实际运行时实现的通用types系统。

换句话说,你总是必须在运行时编组结构来确定布局。 此时从内部布局转换到互操作布局。 如果布局是相同的,这可以是非常快的,当字段需要重新排列时很慢,因为总是需要创build结构的副本。 这个技术术语是blittable ,传递一个blittable结构到本地代码是很快的,因为pinvoke编组可以传递一个指针。

性能也是布尔不是一个单一的核心原因。 有几个处理器可以直接寻址,最小的单元是一个字节。 需要一个额外的指令来捕捉字节,这不是免费的。 而且它从来不是primefaces的。

C#编译器并不害羞地告诉你,它需要1个字节,使用sizeof(bool) 。 这仍然不是一个字段在运行时需要多less字节的奇妙预测,CLR还需要实现.NET内存模型,并承诺简单的variables更新是primefaces的 。 这要求variables在内存中正确alignment,以便处理器可以用一个内存总线周期对其进行更新。 很多时候, bool实际上需要4或8字节的内存。 额外的填充是为了确保下一个成员正确alignment而添加的。

CLR实际上是利用了布局不可发现的优势,它可以优化一个类的布局,重新安排字段,使填充最小化。 所以说,如果你有一个bool + int + bool成员的类,那么将需要1 +(3)+ 4 + 1 +(3)个字节的内存,(3)是填充,总共12字节。 50%的浪费。 自动布局重新排列为1 + 1 +(2)+ 4 = 8个字节。 只有一个类具有自动布局,结构默认具有顺序布局。

更糟糕的是,一个bool可能需要多达32个字节的C ++程序编译,支持AVX指令集的现代C ++编译器。 这强加了32字节的alignment要求,boolvariables可能以31个字节的填充结束。 此外,.NET抖动不发出SIMD指令,除非明确包装,它不能得到alignment保证的核心原因。

首先,这只是互操作的规模。 它不代表数组托pipe代码中的大小。 这是每个bool 1个字节 – 至less在我的机器上。 您可以使用以下代码自行testing:

 using System; class Program { static void Main(string[] args) { int size = 10000000; object array = null; long before = GC.GetTotalMemory(true); array = new bool[size]; long after = GC.GetTotalMemory(true); double diff = after - before; Console.WriteLine("Per value: " + diff / size); // Stop the GC from messing up our measurements GC.KeepAlive(array); } } 

现在,按照价值对数组进行编组, 文档说:

当MarshalAsAttribute.Value属性设置为ByValArray ,必须设置SizeConst字段来指示数组中元素的数量。 当需要区分stringtypes时, ArraySubType字段可以select包含数组元素的UnmanagedType 。 只能在其元素在结构中显示为字段的数组上使用此UnmanagedType

所以我们看一下ArraySubType ,它的文档是:

您可以将此参数设置为UnmanagedType枚举中的值,以指定数组元素的types。 如果未指定types,则使用与受pipearrays的元素types对应的默认非托pipetypes。

现在看UnmanagedType ,有:

布尔
一个4字节的布尔值(true!= 0,false = 0)。 这是Win32 BOOLtypes。

所以这是bool的默认bool ,它是4字节,因为它对应于Win32 BOOLtypes – 所以如果你正在与期望BOOL数组的代码进行交互操作,那么它完全是你想要的。

现在,您可以将ArraySubType指定为I1而将其logging为:

一个1字节的有符号整数。 您可以使用此成员将布尔值转换为1字节的C样式bool(true = 1,false = 0)。

因此,如果您正在进行互操作的代码需要每个值1个字节,请使用:

 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.I1)] public bool[] values; 

您的代码将显示为每个值占用1个字节,如预期的那样。