结构中 C# 固定布尔数组的大小和对齐方式是什么?

What's the size and alignment of C# fixed bool array in struct?

做P/Invoke时,数据布局匹配很重要。

我们可以通过使用一些属性来控制结构的布局。

例如:

struct MyStruct 
{
    public bool f;
}

的大小为 4。虽然我们可以告诉编译器将其设为 1 字节布尔值以匹配 bool 的 C++ 类型:

struct MyStruct
{
    [MarshalAs(UnmanagedType.I1)]
    public bool f;
}

大小为 1。

这些有道理。但是当我测试fixed bool 数组时,我很困惑。

unsafe struct MyStruct
{
    public fixed bool fs[1];
}

给出 4 个字节的大小。和

unsafe struct MyStruct
{
    public fixed bool fs[4];
}

仍然给出 4 个字节的大小。但是

unsafe struct MyStruct
{
    public fixed bool fs[5];
}

大小为 8。

看起来在固定的bool数组中,bool元素的大小仍然是1个字节,但是对齐是4个字节。这与 1 字节大小和对齐方式的 C++ bool 数组不匹配。

有人可以解释一下吗?

更新:我终于明白了,原因是,在结构中输入bool,那么该结构将永远不会被blittable!所以不要指望内部具有 bool 类型的结构与 C 中的布局相同。

此致, 湘.

A bool比较特殊,这要追溯到Dennis Ritchie决定不给C语言一个bool类型。这造成了很多混乱,语言和操作系统设计者自己添加了它并做出了不兼容的选择。

它作为 BOOL typedef 添加到 Winapi。如果您不强制使用另一种类型,那么这是默认的封送处理。 Typedef-ed as int 以保持它与 C 兼容,如您所见,占用 4 个字节。并对齐到 4,正如您所发现的,就像任何 int 一样。

它已添加到 C++ 中。在没有大小规范的情况下,大多数 C++ 编译器实现选择单个字节进行存储。最引人注目的是 Microsoft C++ 编译器,它是您最有可能与之互操作的实现。

它作为 VARIANT_BOOL 添加到 COM Automation。最初的目标是作为 Visual Basic 的新扩展模型来摆脱 VBX 限制,它变得非常流行,现在 Windows 上的几乎所有语言运行时都支持它。 VB 当时受到 16 位操作系统敏感性的严重影响,VARIANT_BOOL 占用 2 个字节。

所有三个本机运行时环境都可能是 C# 程序中互操作的目标。显然,CLR 设计者需要做出一个非常困难的选择,必须在 1、2 和 4 个字节之间做出选择。没有办法取胜,虽然 CLR 确实有机会猜测 COM 互操作,但它无法知道您是否尝试与 C-based api 或 C++ 程序互操作。所以他们做出了唯一合乎逻辑的选择:none 个。

包含 bool 的结构或 class 类型永远不会 blittable。即使您应用 [MarshalAs(UnmanagedType.U1)],也不会使其与 CLR 类型兼容。不太确定这是一个好的决定,但这是他们做出的决定,所以我们必须处理它。

非常需要获得一个 blittable 结构,它可以避免复制。它允许本机代码直接访问托管堆和堆栈。非常危险,许多损坏的 pinvoke 声明已经破坏了 GC 堆,而没有 unsafe 关键字警告的通常好处。但速度无可匹敌。

您通过 而不是 使用 bool 获得了一个 blittable 结构。请改用 byte。您仍然可以通过用 属性 包装结构成员来取回布尔值。不要使用 auto-implemented 属性,你必须关心字节的位置。因此:

struct MyStruct 
{
    private byte _f;
    public bool f {
        get { return _f != 0; }
        set { _f = value ? 1 : 0; }
    }
}

本机代码忽略了 属性。不要担心 getter 和 setter 的运行时开销,抖动优化器使它们消失,并且它们各自变成一条 CPU 指令。

应该有效:

[StructLayout(LayoutKind.Sequential)]
unsafe struct MyStruct
{
   public fixed bool fs[5];
}