内存中.NET值类型的布局

我有以下.NET值类型:

[StructLayout(LayoutKind.Sequential)] public struct Date { public UInt16 V; } [StructLayout(LayoutKind.Sequential)] public struct StringPair { public String A; public String B; public String C; public Date D; public double V; } 

我有代码将指向值类型的指针传递给非托管代码,以及通过调用System.Runtime.InteropServices.Marshal.OffsetOf发现的偏移量。 非托管代码填充Date和double值。

为StringPair结构报告的偏移正是我所期望的:0,8,16,24,32

我在测试函数中有以下代码:

 FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public); for ( int i = 0; i > field {0} @ offset {1}", fields[i].Name, offset)); } 

这打印出这些偏移。

  >> field A @ offset 0 >> field B @ offset 8 >> field C @ offset 16 >> field D @ offset 24 >> field V @ offset 32 

然后我有一些测试代码:foreach(成对的StringPair对){Date d = pair.D; double v = pair.V; …

在调试器中有以下汇编程序与之关联:

  Date d = pair.D; 0000035d lea rax,[rbp+20h] 00000361 add rax,20h 00000367 mov ax,word ptr [rax] 0000036a mov word ptr [rbp+000000A8h],ax 00000371 movzx eax,word ptr [rbp+000000A8h] 00000378 mov word ptr [rbp+48h],ax double v = pair.V; 0000037c movsd xmm0,mmword ptr [rbp+38h] 00000381 movsd mmword ptr [rbp+50h],xmm0 

它在偏移32(0x20)加载D字段,在偏移24(0x38-0x20)加载V字段。 JIT改变了订单。 Visual Studio调试器也显示此反转顺序。

为什么!? 我一直在拉我的头发试图看看我的逻辑出错了。 如果我在结构中交换D和V的顺序,那么一切正常,但是这段代码需要能够处理插件架构,其他开发人员已经定义了结构,并且不能期望他们记住神秘的布局规则。

从Marshal类获得的信息仅在类型实际被封送时才相关。 托管结构的内部存储器布局不能通过任何记录的方式发现,除了可能在汇编代码上偷看。

这意味着CLR可以自由地重新组织结构布局并优化包装。 由于双精度的对齐要求,交换D和V字段会使结构变小。 它可以在64位计算机上节省6个字节。

不知道为什么这对你来说是一个问题,它不应该是。 考虑使用Marshal.StructureToPtr()以您想要的方式布局结构。

如果你需要明确的布局… 使用显式布局……

 [StructLayout(LayoutKind.Explicit)] public struct StringPair { [FieldOffset(0)] public String A; [FieldOffset(8)] public String B; [FieldOffset(16)] public String C; [FieldOffset(24)] public Date D; [FieldOffset(32)] public double V; } 

两件事情:

  • StructLayout(Sequential)不保证包装。 您可能希望使用Pack=1 ,否则32位和64位平台可能会有所不同。

  • 和string是引用,而不是指针。 如果字符串长度始终是固定的,您可能希望使用固定字符串数组:

     public struct MyArray // This code must appear in an unsafe block { public fixed char pathName[128]; }