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

次に、いくつかのテスト コードを用意します。ダブル 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 フィールドを交換すると、double のアラインメント要件により、構造が小さくなります。これにより、64 ビット マシンで 6 バイトが節約されます。

なぜこれが問題になるのかわかりませんが、そうすべきではありません。Marshal.StructureToPtr() を使用して、構造を希望どおりに配置することを検討してください。

于 2009-12-16T22:17:03.877 に答える
1

2つのこと:

  • StructLayout(Sequential)梱包を保証するものではありません。を使用することをお勧めしますPack=1。そうしないと、32ビットプラットフォームと64ビットプラットフォームが異なる場合があります。

  • 文字列は参照であり、ポインタではありません。文字列の長さが常に固定されている場合は、固定文字配列を使用することをお勧めします。

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