编组包含布尔值的复杂嵌套结构

时间:2014-05-22 07:31:26

标签: c# c++ struct boolean marshalling

我需要对几个嵌套结构进行复杂的编组,包含可变长度数组到其他结构,因此我决定使用ICustomMarshaler(参见一个好的JaredPar&#39教程here)。但是我在C ++中定义的结构有一个问题:

typedef struct AStruct{
    int32_t     a;
    AType*      b;
    int32_t     bLength;
    bool        aBoolean;
    bool        bBoolean;
};

在C#方面,我正在使用的MarshalManagedToNative ICustomMarshaler实现:

Marshal.WriteByte(intPtr, offset, Convert.ToByte(aBoolean));
offset += 1;
Marshal.WriteByte(intPtr, offset, Convert.ToByte(bBoolean));

但是由于我发现C ++结构中的每个bool都占用了2个字节,所以它无效。确实在x86 sizeof(AStruct) = 16中,而不是14.好吧,bool不能保证占用1个字节,因此我尝试使用unsigned charuint8_t但仍然是16。

现在,我知道我可以使用int32而不是布尔值,但是因为我关心所占用的空间,并且有几个结构包含流向磁盘的布尔值(我使用HDF5文件格式,我想用这些布尔值映射H5T_NATIVE_UINT8在HDF5库中定义,占用1个字节),还有另外一种方法吗?我的意思是我可以在结构内部保留一个字节吗?

修改

同样的问题也适用于int16值:取决于由于对齐原因而存在多少个值,结尾处结构的大小可能与预期的不同。在C#方面,我没有"看到"在C ++结构中,我只是通过遵循C ++中我的结构的定义来编写非托管内存。这是一个非常简单的过程,但如果我反而考虑结构所采用的真实空间(通过猜测或测量它),每次修改结构时都会变得更加困难和容易出错。

2 个答案:

答案 0 :(得分:2)

  

sizeof(AStruct)= 16,而不是14

这是对的。结构在末尾有两个额外的字节,未使用。如果将结构放在数组中,它们确保结构中的字段仍然正确对齐。在32位模式下,int32_tAType*成员需要4个字节,并且应该与4的倍数对齐,以允许处理器快速访问它们。只有当结构大小是4的倍数时才能实现这一点。因此,14被四舍五入为16。

请注意,意味着bool字段占用2个字节。 C ++编译器只使用1个字节。额外的2个字节是纯填充。

如果在C#程序中使用Marshal.SizeOf(typeof(AStruct)),那么你会发现你声明的结构需要20个字节。这不好,你正试图解决的问题。 bool 成员是问题,这个问题一路走来,回到早期版本的C语言。哪个没有bool类型。 CLR使用的默认封送处理与winapi中的typedef BOOL兼容。这是一个32位类型。

因此,当您在C#代码中声明结构时,必须明确它,您必须告诉编组器您需要1字节类型。通过将struct成员声明为 byte 来执行此操作。或者通过覆盖默认编组:

[StructLayout(LayoutKind.Sequential)]
private struct AStruct{
    public int    a;
    public IntPtr b;
    public int    bLength;
    [MarshalAs(UnmanagedType.U1)]
    public bool   aBoolean;
    [MarshalAs(UnmanagedType.U1)]
    public bool   bBoolean;
}

现在您将看到Marshal.SizeOf()现在返回16.请注意,您必须以32位模式强制执行程序,确保EXE项目的Platform Target设置为x86。

答案 1 :(得分:2)

这个答案是Hans Passant所说的。

让您的结构使用固定的包装尺寸可能最容易,因此您可以轻松预测成员布局。请记住,这可能会影响性能。

本答案的其余部分特定于Microsoft Visual C ++,但大多数编译器都提供了自己的变体。

为了帮助您入门,请查看此SO回答#pragma pack effect和MSDN http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx

您经常使用的是pragma pack(push, ...)后跟pragma pack(pop, ...)成语,仅影响两个编译指示之间定义的结构的打包:

#pragma pack(push, 4)
struct someStructure
{
    char a;
    int b;
    ...
};
#pragma pack(pop)

这将使someStructure具有每个成员的4字节对齐的可预测包装。

编辑:从包装上的MSDN页面

  

成员的对齐将位于n的倍数的边界上   或者成员大小的倍数,以较小者为准。

因此对于pack(4)char将在1字节边界上对齐,在{2}字节上对齐short,其余在4字节边界上。

哪个值最好取决于您的情况。您需要明确打包您打算访问的所有结构,以及可能是您要访问的结构成员的所有结构。