代码之家  ›  专栏  ›  技术社区  ›  Rob Walker

内存中.NET值类型的布局

  •  11
  • Rob Walker  · 技术社区  · 16 年前

    我有以下.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;
    }
    

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

    FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public);
    
    for ( int i = 0; i < fields.Length; i++ )
    {
        int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32();
    
        Console.WriteLine(String.Format(" >> 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) { 日期d=配对d; 双v=双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已经改变了订单。VisualStudio调试器也显示了这种相反的顺序。

    为什么?我一直在竭尽全力,想看看我的逻辑哪里出了问题。如果我在结构中交换D和V的顺序,那么一切都可以正常工作,但是这段代码需要能够处理其他开发人员已经定义了结构的插件架构,并且不能期望他们记住神秘的布局规则。

    3 回复  |  直到 14 年前
        1
  •  13
  •   Hans Passant    16 年前

    仅当类型实际被封送时,从封送处理类获得的信息才相关。托管结构的内部内存布局无法通过任何有文档记录的方式发现,可能除了查看汇编代码之外。

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

    不知道为什么这会成为你的问题,不应该。考虑McSal.StultTopTrand()来获得您想要的结构。

        2
  •  15
  •   Marc Gravell    16 年前

    如果您需要显式布局。。。 显式布局。。。

    [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;
    }
    
        3
  •  1
  •   Abel    14 年前

    两件事:

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

    • 字符串是一个引用,而不是指针。如果字符串长度始终固定,则可能需要使用固定字符数组:

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