封送包含布尔值的复杂嵌套结构

本文关键字:复杂 嵌套 结构 包含布 | 更新日期: 2023-09-27 18:04:45

我需要对几个嵌套结构进行复杂的封送处理,其中包含其他结构的可变长度数组,因此我决定使用ICustomMarshaler(请参阅此处的优秀JaredPar教程)。但是我有一个问题,在c++中定义为:

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

在c#端,在ICustomMarshalerMarshalManagedToNative实现中,我使用:

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文件格式,我想映射那些布尔与HDF5库中定义的H5T_NATIVE_UINT8,需要1字节),有没有另一种方式?我的意思是,我能不能在结构体中保证占用1字节?

编辑

同样的问题也适用于int16值:根据由于对齐原因而存在的值的数量,结构体的大小可能与预期的不同。在c#方面,我没有"看到"c++结构体,我只是按照c++结构体的定义在非托管内存上进行编写。这是一个非常简单的过程,但是如果我不得不考虑结构所占用的实际空间(通过猜测或测量它),那么每次修改结构时就会变得更加困难并且容易出错。

封送包含布尔值的复杂嵌套结构

这个答案是对汉斯·帕桑说过的话的补充。

让你的结构使用固定的包装尺寸可能是最简单的,这样你就可以很容易地预测成员的布局。但请记住,这可能会影响性能。

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

要开始,请查看这个SO答案#pragma pack effect和MSDN http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx

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

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

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

编辑:来自MSDN页面上的包装

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

所以对于pack(4), char将在1字节边界上对齐,short在2字节边界上对齐,其余的在4字节边界上对齐。

哪个值最好取决于您的情况。您需要显式地打包您打算访问的所有结构,并且可能是您想要访问的结构的成员的所有结构。

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使用的默认封送处理与BOOL (winapi中的类型定义)兼容。这是一个32位类型。

所以当你在c#代码中声明结构体时,你必须明确它,你必须告诉编组器你想要1字节类型。这可以通过将结构体成员声明为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项目的平台目标设置为x86。