.NET中结构体的大小

9
我的问题是如何在C程序和C#程序之间发送一个结构体。
我在C#中创建了一个结构体:
public struct NetPoint {
    public float lat; // 4 bytes
    public float lon; // 4 bytes
    public int alt; // 4 bytes
    public long time; // 8 bytes
}

这个结构体的总大小必须为20字节。

在C++中,当我对这个结构体使用sizeof()时,

System.Diagnostics.Debug.WriteLine(
    "SizeOf(NetPoint) = " +
    System.Runtime.InteropServices.Marshal.SizeOf(new NetPoint()));

调试控制台显示:

NetPoint的大小为24

但我预期它应该是20字节,为什么会有差异呢?


请查看此处:http://www.vsj.co.uk/articles/display.asp?id=501 - Simon P Stevens
4个回答

10

一般来说,CPU 喜欢将变量在内存中对齐到它们大小的偶数倍位置,因此一个四字节整数应该在可被四整除的内存地址上,八字节的 long 应该在可被八整除的地址上。

C#(和 C++)语言设计者知道这一点,他们会在结构体中插入填充以提供必要的对齐。因此,您的结构的实际布局如下:

public struct NetPoint {
    public float lat;          // 4 bytes   Offset  0
    public float lon;          // 4 bytes   Offset  4
    public int alt;            // 4 bytes   Offset  8
    int to_preserve_alignment; // 4 bytes   Offset 12
    public long time;          // 8 bytes   Offset 16
}

按照惯例,您可以通过将长值放在第一个位置来解决此问题。如果您总是将最大值放在结构的开头,就不会插入任何填充以保留成员对齐的对齐方式。

您还可以通过添加

[StructLayout(LayoutKind.Sequential, Pack = 4)]

在结构声明前使用 __attribute__((packed)) 可以达到紧凑的效果,但这会导致不对齐的 long time,进而影响性能。对于某些CPU而言,这将严重降低性能。(例如 ALPHA AXP 将因为成员不对齐而出现故障)。 x86 CPU 的性能惩罚较小,但未来的CPU可能会有很大的性能惩罚,所以最好设计你的结构使其正确对齐 (而不是紧凑地打包)。


1
哇,我喜欢这种反馈。我刚学到了一些新的有趣的东西。 - Keith Adler

7
实际上,技术上结构体必须至少为20字节。如果在发送时分配更多,接收方将不会使用/复制它们。问题始终是下分配。
话虽如此,我看到了问题所在。嗯。我认为问题在于最后一个长的... 它在我的IMHO中被对齐到八个字节,在之前注入了四个空字节。我认为具有未对齐到八字节边界的八字节元素会有性能惩罚。
附加StructLayout属性以手动确定每个元素的偏移量。然后您应该能够让事情排列起来。
参考:.NET Framework 2.0中如何控制数据字段的物理布局
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct NetPoint {  
    public float lat; // 4 bytes 
    public float lon; // 4 bytes 
    public int alt; // 4 bytes 
    public long time; // 8 bytes 
} 

至少应该将元素对齐到一个字节边界。如果需要,您还可以进一步定义每个元素的确切起始位置。


你的解决方案也可以。非常感谢你。你的解决方案似乎比约翰的答案更简单。我会使用你的解决方案。再见。 - user291830

2
尝试添加属性[StructLayout(LayoutKind.Sequential, Pack = 1)],看看会发生什么。我怀疑存在8字节的填充,所以它是3x8字节。

1
除了像 int 这样的基本类型,.NET 中的对象大小不仅仅是其字段大小之和。 - Andrey

1
TomTom已经很好地回答了这个问题,但如果你遇到了棘手的COM互操作结构情况,还有另一种选择。每个字段都可以使用FieldOffset属性单独对齐。
[StructLayout(LayoutKind.Explicit)]
public struct COMPoint
{
    [FieldOffset(0)] public int X;
    [FieldOffset(4)] public int Y;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接