【问题标题】:What's the size and alignment of C# fixed bool array in struct?struct中C#固定bool数组的大小和对齐方式是多少?
【发布时间】:2016-01-07 05:20:05
【问题描述】:

在进行P/Invoke时,数据布局匹配很重要。

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

例如:

struct MyStruct 
{
    public bool f;
}

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

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

大小为 1。

这些是有道理的。但是当我测试固定的布尔数组时,我很困惑。

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 中的布局相同。

问候, 向。

【问题讨论】:

    标签: c# pinvoke


    【解决方案1】:

    bool 相当特别,它可以追溯到 Dennis Ritchie 决定不给 C 语言一个 bool 类型。这引起了很多混乱,语言和操作系统设计者自己添加了它并做出了不兼容的选择。

    它作为 BOOL typedef 添加到 Winapi。如果您不强制使用其他类型,那将是默认的封送处理。类型定义为 int 以使其与 C 兼容,您发现它需要 4 个字节。正如您发现的那样,它与 4 对齐,就像任何 int 一样。

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

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

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

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

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

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

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

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

    【讨论】:

    • private bool _f; -> private byte _f;?
    • 很高兴我解释得很好:) 谢谢。
    • 谢谢,这是一个非常详细的解释。虽然我知道 C++ 中的一个属性可能是一个健壮的解决方案,但我仍然想知道为什么在 C# 固定 bool 数组中,它的行为很奇怪,看起来它的大小是 1 字节,但它的对齐方式是 4 字节。你知道嵌入在结构中的固定 bool 数组的 C# 编组背后有什么魔力吗?
    • 固定缓冲区在 CLR 领域是非常奇怪的动物,它被视为没有成员的自定义值类型。只是一团记忆。解释缓冲区元素类型的强限制,编译器需要在编译时知道大小,以便正确索引 blob。只有简单的值类型可以。与 C++/CLI 语言更相关的是,这种值类型可以存储本机 C++ 对象。这是对齐选择的可能原因。理想情况下它会对齐到 8,就像 C++ 对象一样,但是 32 位 CLR 不能提供这种保证,它只支持 4。
    • 但是我检查了 unsafe struct { fixed short f[1] },大小为 2,{ fixed byte f[1] },大小为 1。所有这些数字类型都是固定的数组按预期工作,布尔类型除外。我认为 bool 也应该是非常基本的类型,它可以是 4byte 或 1 byte,但据我观察,它是 size 1 和 align 4,这很奇怪。不管怎样,谢谢你的解释。
    【解决方案2】:

    应该有效:

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

    【讨论】:

    • 我测试了,竟然加了这个顺序布局属性,bool[3] size=4, bool[4] size=4, bool[5] size=8, 一样。
    • 我也是:) 你试过 [StructLayout(LayoutKind.Sequential, Pack = 1)] 吗?看起来在项目的某个地方,您有一个选项可以将结构的默认对齐方式设置为 4 个字节
    猜你喜欢
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-01
    相关资源
    最近更新 更多