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

标签 c# pinvoke

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

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

例如:

struct MyStruct 
{
    public bool f;
}

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

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

大小为 1。

这些是有道理的。但是当我测试固定的 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 中的布局相同。

问候, 翔。

最佳答案

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

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

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

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

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

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

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

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

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

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

关于c# - 结构中 C# 固定 bool 数组的大小和对齐方式是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34647868/

相关文章:

c# - IList<T>、IEnumerable<T> 和 ICollection<T> 的 TypeDependencyAttribute ("System.SZArrayHelper"的用途?

c# - Winforms控件定位

c# - 如何使用 p/invoke 编码 Delphi 短字符串?

c# - 了解使用固定{}、Marshal.AllocHGlobal() 和 GCHandle.Alloc() 之间的区别

c# - 如何从 C# 调用 C++ DLL 函数

c# - 如何将此条件写入 NHibernate HBM 映射?

c# - 您可以将数据绑定(bind)到包含 Silverlight 参数的属性吗?

c# - Linq 选择嵌套行

c# - 如何将结构传递给 C++ 函数并通过一些修改返回相同的结构?

c# - 用于复杂方法调用的 PInvoke C#