在进行P/Invoke时,重要的是使数据布局匹配.
我们可以使用一些属性来控制struct的布局.
例如:
struct MyStruct { public bool f; }
给出大小为4.虽然我们可以告诉编译器使它成为1字节bool以匹配C++类型bool
:
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个字节.这与C++ bool数组不匹配,后者是1字节大小和对齐方式.
有人能解释一下吗?
更新:我终于找到了,原因是,bool类型在一个结构中,那么该结构永远不会是blittable!因此,不要指望内部具有bool类型的结构与C中的布局相同.
问候,翔.
一个布尔值比较特殊,它可以追溯到丹尼斯里奇决定给不给C语言的布尔类型.这引起了大量的混乱,语言和操作系统设计者自己添加了它并做出了不兼容的选择.
它被添加到Winapi作为BOOL typedef.如果您不强制使用其他类型,则这是默认的封送处理.Typedef-ed为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的api或C++程序互操作.所以他们做出了唯一合乎逻辑的选择:没有一个.
包含bool的结构或类类型永远不会是blittable.即使应用[MarshalAs(UnmanagedType.U1)],也不会使它与CLR类型兼容.不太确定这是一个很好的决定,但它是他们制作的那个,所以我们必须处理它.
获得一个blittable结构是非常需要的,它避免了复制.它允许本机代码直接访问托管堆和堆栈.非常危险且许多破坏的pinvoke声明已经损坏了GC堆,而没有不安全关键字警报的通常好处.但不可能为速度而战.
你不使用就得到了一个blittable结构bool
.请byte
改用.您仍然可以通过使用属性包装struct成员来获取bool.不要使用自动实现的属性,您必须关心字节的位置.从而:
struct MyStruct { private byte _f; public bool f { get { return _f != 0; } set { _f = value ? 1 : 0; } } }
本机代码不知道该属性.不要担心getter和setter的运行时开销,抖动优化器使它们消失,并且它们每个变为单个CPU指令.