3

フィールドを含む順序付けられた構造体があります..

[StructLayout(LayoutKind.Explicit)]
public unsafe struct RunBlock_t {
    [System.Runtime.InteropServices.FieldOffset(0)]  public fixed byte raw[512];
}

これを関数内で宣言し、ポインターを使用したい場合は、正常に動作します..

{
  RunBlock_t r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

しかし、変数をスコープ外で宣言すると、固定の実装が必要になります

RunBlock_t r;
{
  r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<510; i++) ptr[i]=0;
}

この動作の違いはなぜですか?

---編集済み-----

他の順列は機能しないことをもう一度述べたいだけです。

    unsafe void foo() {
        RunBlock_t r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 512; i++) ptr[i] = 0;
    }

生成する fixed ステートメントを使用して、既に固定されている式のアドレスを取得することはできず、コンパイルされません。

    RunBlock_t r;
    unsafe void foo() {
      r = new RunBlock_t();
      for (int i=0; i<512; i++) r.raw[i]=0;
    }

生成固定されていない式に含まれる固定サイズのバッファーは使用できません。固定ステートメントを使用してみてください。コンパイルされません。

4

2 に答える 2

5

残念ながら、あなたは質問を少し混乱させました。質問から正確にコピーすると、これは正常に機能します。

    {
        RunBlock_t r = new RunBlock_t();
        for (int i = 0; i < 512; i++) r.raw[i] = 0;
    }

この:

    RunBlock_t r;
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }

レイズ:

fixed ステートメントを使用して、既に固定されている式のアドレスを取得することはできません

を削除するfixedと、機能します。

あなたが示すべきだったのは、関数の署名、つまり

RunBlock_t r;
unsafe void Bar()
{
    {
        r = new RunBlock_t();
        fixed (byte* ptr = r.raw) for (int i = 0; i < 510; i++) ptr[i] = 0;
    }
}

これで意味がより明確になります。fixed値で固定バッファにアクセスするために使用する場合、実際にはバッファを修正していませんし、も修正していません。実際に修正しているのは、包含オブジェクト、つまりrフィールドを持つオブジェクトです。これは、GC がスタック上でそれを移動するのを防ぐためです。これは、その時点でポインターとしてアクセスしている場合には良くありません。上記の例では、式は reallyfixed (byte* ptr = this.r.raw)であり、固定されるのは:thisです。

ローカルとして構造体しかない場合、これは異なります。ローカルはスタックにあります。それらは (以前のメッセージが示唆しているように)既に修正されています。GC によってスタックが再配置されることはありません。

そう:

  • 構造体をローカル変数として持っている場合は、使用する必要はありません- ポインタとして (経由で)fixed直接アクセスしているだけです。ldloca
  • オブジェクトのフィールドである構造体がある場合はfixed、を使用して、操作中にオブジェクトを所定の位置に固定する必要があります
  • 構造体 (つまりパラメーター)への参照を渡す場合は、それがオブジェクトのフィールドである場合に備えて使用する必要があります。参照がスタックに解決されることが判明した場合、何もする必要はありませんref RunBlock_tfixed
  • 「オブジェクトのフィールド」に関するここでのすべては、「配列の値」にも等しく適用されることに注意してくださいsomeArray[8]
于 2012-10-27T18:39:43.823 に答える
5

あなたの質問は意味がありません。おそらく C および C++ では配列とポインターを混同しており、[]添字演算子を含むほとんどのコンテキストで配列はすぐにポインターに分解されます。

しかし、これはC#です。配列とポインターは完全に別個の獣です (配列をポインターに強制することはできますがfixed、ポインターが有効であることを確認するために、ステートメントで強制する必要があります)。比較してるはず

{
  RunBlock_t r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

RunBlock_t r;
{
  r = new RunBlock_t();
  for (int i=0; i<512; i++) r.raw[i]=0;
}

どちらも配列を使用します。またはそうでなければ

{
  RunBlock r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}

RunBlock_t r;
{
  r = new RunBlock_t();
  fixed (byte* ptr = r.raw) for (int i=0; i<512; i++) ptr[i]=0;
}

どちらもポインターを使用します。

そして、変数が宣言されているスコープは、必要とはまったく関係がないことがわかりますfixed

于 2012-10-26T22:45:40.007 に答える