13

我有以下 .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 < 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 pair in pairs) { Date d = pair.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 改变了顺序。Visual Studio 调试器也显示了这种倒序。

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

4

3 回答 3

15

如果您需要显式布局...使用显式布局...

[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;
}
于 2009-12-16T21:53:44.797 回答
13

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

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

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

于 2009-12-16T22:17:03.877 回答
1

两件事情:

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

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

    public struct MyArray // This code must appear in an unsafe block
    {
        public fixed char pathName[128];
    }
    
于 2011-03-13T09:00:31.253 回答