C# 模仿C++;C中带并集的嵌套结构#

C# 模仿C++;C中带并集的嵌套结构#,c#,c++,struct,C#,C++,Struct,我知道这个问题以前已经被问过很多次了,我试着把前面的问题通读一遍,但运气不好 我尝试将下面的C++结构转换成C语言,用于socket通信。 enum class packet_type { read_mem, get_base_addr, get_pid, completed }; struct copy_mem { unsigned int dest_process_id; unsigned long long dest_address;

我知道这个问题以前已经被问过很多次了,我试着把前面的问题通读一遍,但运气不好

我尝试将下面的C++结构转换成C语言,用于socket通信。
enum class packet_type
{
    read_mem,
    get_base_addr,
    get_pid,
    completed
};

struct copy_mem
{
    unsigned int dest_process_id;
    unsigned long long dest_address;

    unsigned int src_process_id;
    unsigned long long src_address;

    unsigned int size;
};

struct get_base_addr
{
    unsigned int process_id;
};

struct get_pid
{
    size_t len;
    wchar_t name[256];
};

struct completed
{
    unsigned long long result;
};

struct PacketHeader
{
    //uint32_t   magic;
    packet_type type;
};

struct Packet
{
    PacketHeader header;
    union
    {
        copy_mem     copy_memory;
        get_base_addr get_base_address;
        get_pid get_pid;
        completed        completed;
    } data;
};
这是我目前的C#实现

public enum PacketType
{
    read_mem = 0,
    get_base_addr = 1,
    get_pid = 2,
    completed = 3
}

[StructLayout(LayoutKind.Sequential)]
public struct PacketHeader
{
    public PacketType type;
}

[StructLayout(LayoutKind.Sequential)]
public struct get_base_addr
{
    uint process_id;
};

[StructLayout(LayoutKind.Sequential)]
public struct get_pid
{
    public ulong len;

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
    public string name;
}

[StructLayout(LayoutKind.Sequential)]
public struct copy_mem
{
    public uint dest_process_id;
    public ulong dest_address;

    public uint src_process_id;
    public ulong src_address;
 
    public uint size;
}

[StructLayout(LayoutKind.Sequential)]
public struct completed
{
    public ulong result;
};

[StructLayout(LayoutKind.Explicit, Pack = 0, CharSet = CharSet.Unicode)]
public struct Packet
{
    [FieldOffset(0)] //
    public PacketHeader header;

    [FieldOffset(4)]
    public copy_mem CopyMem; //28

    [FieldOffset(32)]
    public get_base_addr GetBaseAddress;

    [FieldOffset(36)]
    public get_pid GetPid;

    [FieldOffset(300)]
    public completed Completed;
}
然后,我使用此方法将结构转换为用于套接字传输的字节数组:

       public static byte[] RawSerialize(T item)
    {
        int rawSize = Marshal.SizeOf(typeof(T));
        IntPtr buffer = Marshal.AllocHGlobal(rawSize);
        var a = Marshal.SizeOf(item);
        var b = Marshal.SizeOf(buffer);

        Marshal.StructureToPtr(item, buffer, false);
        byte[] rawData = new byte[rawSize];
        Marshal.Copy(buffer, rawData, 0, rawSize);
        Marshal.FreeHGlobal(buffer);
        return rawData;
    }

问题是
var a=Marshal.SizeOf(项目)
报告的大小为
312
,但在C++中执行
sizeof(Packet)
时,实际的结构应该是
528
字节。您的假设似乎是错误的。首先,
wchar\u t
类型在不同的机器上可能具有不同的长度。在我的x64 Linux机器上,它是4个字节——这就使得
get_pid
a
1032
字节大小的结构。您可能对使用
char16\u t
char32\u t
类型感兴趣(请参见示例)

由于
数据包
中的
联合
与所有字段重叠,这也使得
数据包
成为一个
1040
字节大小的结构:
4
字节用于
PacketHeader
1032
字节用于
get\u pid
,这是迄今为止最长的结构,并且
4
字节用于填充。遗憾的是,填充是特定于平台的

为了摆脱C/C++编译器中的填充,您需要使用诸如GCC的
\uuuu属性(packed))
或Visual C++的
\pragma pack(1)
(参见示例SO答案)之类的属性

小心,C中的字段偏移也是错误的:除了标题,代码< >包<代码>中的所有字段偏移必须是代码> [FieldS偏移(4)] < /C> >因为在C++中,它是<代码>联合< <代码>,开始于字节<代码> 4代码>(假设为零填充)。 对于可移植性,还要注意

无符号long-long
也是特定于平台的,唯一的保证是至少64位长。如果您恰好需要64位,则可能需要使用
uint64\u t
(参见示例)


以下是我用来确定大小的代码(Linux x64,GCC 9.3):

intmain(){

STD::C++Stutt中的所有字段都共享相同的存储空间,在C++中如何实现这一点?我不知道,这就是我为什么来这里的原因:它们有相同的偏移量。EnUM类也不是跨语言兼容的。网络协议明确地定义了字段大小。谢谢你们的详细响应。如果我改变了<代码>字段偏移< /COD>所有的S在
数据包
中,我在运行应用程序时得到了这个消息-
数据包无效…因为它在偏移量4处包含一个对象字段,该字段与非对象字段不正确对齐或重叠。
罪魁祸首是
get_pid
结构的
名称
字段,我能说的就这么多。
packet_type:   4
copy_mem:      40
get_base_addr: 4
get_pid:       1032
completed:     8
PacketHeader:  4
Packet:        1040
wchar_t:       4
packet_type:   4
copy_mem:      28
get_base_addr: 4
get_pid:       1032
completed:     8
PacketHeader:  4
Packet:        1036
wchar_t:       4
Unhandled exception. System.TypeLoadException: Could not load type 'Packet' from assembly 'StructSize, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field.