3

以下に、コメントアウトされた1行のコードブロックがあります。メソッドで行われることCreateArrayは、コメント化された行が行うことと同じです。私の質問は、行がコメントされていないときになぜそれが機能するのにb->ArrayItems = d、コメントアウトされたときにゴミを返すのですか?すべての情報が管理されていないため、何も「修正」する必要はないと思います。この仮定は間違っていますか?

class Program
{
    unsafe static void Main(string[] args)
    {
        someInstance* b = stackalloc someInstance[1];
        someInstance* d = stackalloc someInstance[8];

        b->CreateArray();
//      b->ArrayItems = d;

        *(b->ArrayItems)++ = new someInstance() { IntConstant = 5 };
        *(b->ArrayItems)++ = new someInstance() { IntConstant = 6 }; 

        Console.WriteLine((b)->ArrayItems->IntConstant);
        Console.WriteLine(((b)->ArrayItems - 1)->IntConstant);
        Console.WriteLine(((b)->ArrayItems - 2)->IntConstant);
        Console.Read();
    }
}

public unsafe struct someInstance
{
    public someInstance* ArrayItems;
    public int IntConstant;
    public void CreateArray()
    {
        someInstance* d = stackalloc someInstance[8];
        ArrayItems = d;
    }
}
4

3 に答える 3

13

私の質問は、行がコメントされていないときになぜそれが機能するのか、コメントアウトされたときにゴミを返すのかということです。

コメント行は、CreateArrayによって引き起こされたバグを隠しているものです。コメントアウトすると、バグが明らかになります。しかし、バグは関係なくあります。

仕様が明確に述べているように:

関数メンバーの実行中に作成されたすべてのスタック割り当てメモリブロックは、その関数メンバーが戻るときに自動的に破棄されます。

CreateArray関数はブロックを割り当て、ブロックへのポインターを格納し、ブロックは破棄され、ガベージブロックへのポインターが作成されます。ブロックが無効になった後にストレージにアクセスできるように、stackallocされたブロックへのポインターを決して格納しないようにする必要があります。ブロックへの参照を格納する必要がある場合は、ブロックのヒープ割り当てを行い、完了したらブロックの割り当てを解除することを忘れないでください。

安全でないコードでは、マネージドメモリモデルに関するすべてを完全に理解する必要があることを忘れないでください。 すべて。管理対象メモリについてすべてを理解していない場合は、安全でないコードを記述しないでください。

そうは言っても、「ポインタを取得するためにいつメモリを修正する必要があるのか​​」という、より大きな混乱のように思われるものに対処しましょう。答えは簡単です。それが可動メモリである場合に限り、メモリを修正する必要があります。 修正すると、可動メモリが不動メモリに変換されます。それが修正の目的です。

動かせないもののアドレスしか取得できません。あなたが移動可能な何かのアドレスを取り、それが移動する場合、明らかにアドレスは間違っています。アドレスを取得する前にメモリが移動しないようにする必要があります。また、アドレスが再び移動可能になった後は、そのアドレスを使用しないようにする必要があります。

于 2010-07-26T22:48:40.543 に答える
1

Stackallocは、コールスタックにスペースを割り当てます。現在のコンテキストレベルから上に移動すると(たとえば、メソッドを終了すると)、そのスペースは失われます。問題は、stackallocがメソッド内にある場合、そのメソッドを離れると、スタックのその領域を操作できなくなることです。

したがって、これを行う場合:

foo()
{
    stuff = stackalloc byte[1]
    Do something with stuff
}

「stuff」はfoo内でのみ有効です。fooを離れると、スタックは巻き戻されます。つまり、これを行うと、次のようになります。

foo()
{
    byte* allocate()
    {
        return stackalloc[1]
    }

    stuff = allocate()
    do something with stuff
}

その場合、allocateメソッドを終了すると、allocateの戻り値はごみになります。つまり、「もの」は意味をなさないということです。

于 2010-07-26T22:49:55.263 に答える
1

あなたの仮定は部分的に正しいですが、正しく理解されていません。このMSDNページからの引用は次のとおりです。

安全でないモードでは、スタックにメモリを割り当てることができます。この場合、メモリはガベージコレクションの対象ではないため、固定する必要はありません。詳細については、 stackalloc を参照してください。

一部のステートメントはスタックに変数を自動的に割り当てます(つまり、メソッド内の値型)。その他のステートメントは、を使用して具体的に指定する必要がありますstackalloc

メソッドが終了すると、スタックに割り当てられたメモリが破棄されるため、問題が発生します(これを私の前に書いたEric Lippertsの回答を参照してください)。

于 2010-07-26T22:54:53.160 に答える