2009-05-08 6 views
4

配列でFieldOffsetを正しく使用すると少し問題があります。私は、例えば、シリアル化されたバイト配列に「データ」という名前の配列を設定した場合配列内のC#StructLayout/FieldOffsetとインデックス付け

[StructLayout(LayoutKind.Explicit)] 
public struct IndexStruct { 
    [FieldOffset(0)] 
    public byte[] data; 

    [FieldOffset(0)] 
    public short[] idx16; 

    [FieldOffset(0)] 
    public int[] idx32; 
} 

、その後は、「使用してショートパンツなどのデータを取得しよう:以下のコードは、それが私のために正常に動作しない例です。 idx16 "フィールドでは、インデックスはまだバイト[]として整列されています。 idx16 1が、2番目の16ビットワード(バイト2と3)ではなく、データの2番目のバイトをフェッチすることを意味します。バイトの代わりに逆Iインデックスの短絡を行うと、オフセットの配置がソースデータから継承されます。私の質問、これを回避する方法はありますか?要素のサイズを掛けてインデックス値を補うことができますが、別の方法がありますか?

Here私がここでStackOverflowで見つけた答えですが、そのコードを試してみると正しく動作していないことが判明しました。 VSでユニットテストを使用して、次のコードを使用して試しました。

[TestMethod()] 
public void SumTest() { 
    float[] fArr = {2.0f, 0.5f, 0.0f, 1.0f}; 
    MemoryStream ms = new MemoryStream(); 
    for (int i = 0; i < fArr.Length; i++) { 
     ms.Write(BitConverter.GetBytes(fArr[i]), 0, sizeof(float)); 
    } 
    byte[] buff = ms.ToArray(); 
    double expected = 3.5f; 
    double actual = Sum(buff); 
    Assert.AreEqual(expected, actual); 
} 

事前に感謝します。

+0

。もちろん、サイズでインデックスを分割する方が安全でないコードよりも好ましいかもしれません; -p –

答えて

4

問題は(私が知る限りでは)配列の参照を結合しているので、最後に設定された配列が勝ちます。配列があると、インデクサー(バイトオフセットではありません)を使用しています。サイズは関係ありません。

(場合に応じて、または不適切な)「適切に」これを行う方法はおそらく危険なコードとなりますが - 配列へのポインタを取る - のようなもの:

もちろん
IndexStruct s = new IndexStruct(); 
    s.data = new byte[] { 1, 0, 0, 0, 1, 1 }; 

    unsafe 
    { 
     fixed (short* data = s.idx16) 
     { 
      Console.WriteLine(data[0]); // should be 1 (little-endian) 
      Console.WriteLine(data[1]); // should be 0 
      Console.WriteLine(data[2]); // should be 257 
     } 
    } 

、私は私はそれをお勧めしているのか分かりませんが、それはあなたが望むものを達成するようです。

は、私はまた、あなたが完全にstructをドロップすると、ちょうど直接byte[]への危険なアクセスを使用できるかどうかを疑問に思う:あなたのデータ要素のそれぞれは、構造体の内部にあるどこFieldOffsetが定義されて

byte[] raw = new byte[] { 1, 0, 0, 0, 1, 1 }; 
    unsafe 
    { 
     fixed (byte* addr = raw) 
     { 
      short* s = (short*)addr; 
      Console.WriteLine(s[0]); // should be 1 
      Console.WriteLine(s[1]); // should be 0 
      Console.WriteLine(s[2]); // should be 257 
     } 
    } 
+0

はい、うまくいくでしょう。私はそれを使うことを考えましたが、安全でないコードに飛び込むのを避ける方法があれば、しかし、あなたの方法は良いですし、他のすべてが失敗した場合、私はあなたの推薦を使用します。 – Burre

+0

@Burre安全でないコードよりはるかに問題がある場合は、管理参照を再解析してください。あなたが低レベルのものを行うならば、安全でないコードを避ける理由はありませんが、CLRタイプのシステムを完全に変倒させるのを避けることはEVILです。あなたの再解釈のキャストは未定義の動作につながり、CLRの将来のバージョンでは壊れる可能性があります。 – CodesInChaos

+1

@CodeInChaosまたは実際にはWinRT –

-2

..

それらをすべて0に設定すると、それらはすべて0の位置にあることをコンパイラーに知らせることになります。

2番目のことは、バイト、shorts、およびintの配列を作成していることです。

を参照してください:あなたのコメントを再MSDN StructLayoutAttribute

[StructLayout(LayoutKind.Explicit)] 
public struct IndexStruct { 
     [FieldOffset(0)] 
     public byte[16] data; 

     [FieldOffset(16)] 
     public short idx16; 

     [FieldOffset(18)] 
     public int idx32; 
} 
+0

3つのフィールドが同じメモリ領域を指しているようにするには、それらを0の位置に重ねることが意図的です。私は、idx16でバイト値を取得すると、バイトではなくショートとしてバイトを整列させることを期待していましたが、これはうまくいかないように思えます。 – Burre

関連する問題