内存中.NET值类型的布局

Rob*_*ker 13 .net c# jit marshalling value-type

我有以下.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;
}
Run Code Online (Sandbox Code Playgroud)

我有代码将指向值类型的指针传递给非托管代码,以及通过调用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));
}
Run Code Online (Sandbox Code Playgroud)

这打印出这些偏移.

 >> field A @ offset 0
 >> field B @ offset 8
 >> field C @ offset 16
 >> field D @ offset 24
 >> field V @ offset 32
Run Code Online (Sandbox Code Playgroud)

然后我有一些测试代码: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 
Run Code Online (Sandbox Code Playgroud)

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

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

Mar*_*ell 14

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

[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;
}
Run Code Online (Sandbox Code Playgroud)

  • 我可以发誓我在最近几天的某个时候曾尝试过这样做但现在似乎确实有效.但是,重点是顺序*应该*工作,框架报告的偏移量与它实际使用的偏移量不匹配.我真的不希望这个插件架构的用户必须指定一个属性并自己做数学运算才能使它工作. (2认同)

Han*_*ant 12

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

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

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