11

stackalloc以下のような参照型で使用される場合

var arr = stackalloc string[100];

エラーがあります

アドレスの取得、サイズの取得、またはマネージ型 ('string') へのポインターの宣言はできません

どうしてですか?CLRマネージド型へのポインターを宣言できないのはなぜですか?

4

4 に答える 4

1

C++ とは対照的に、C# はメモリの安全性のためにガベージ コレクションを処理するため、メモリ管理のニュアンスを知っている必要があります。

たとえば、次のコードを見てください。

public static void doAsync(){
    var arr = stackalloc string[100];
    arr[0] = "hi";
     System.Threading.ThreadPool.QueueUserWorkItem(()=>{
           Thread.Sleep(10000);
           Console.Write(arr[0]);
     });
}

プログラムは簡単にクラッシュします。スタックが割り当てられているためarr、オブジェクトとそのメモリは、終了するとすぐに消えdoAsyncます。ラムダ関数は、この無効なメモリアドレスをまだ指しており、これは無効な状態です。

参照によってローカル プリミティブを渡すと、同じ問題が発生します。

スキーマは次のとおりです。
静的オブジェクト -> applocation 時間全体にわたって存続
ローカル オブジェクト -> それらを作成したスコープが有効な
ヒープ割り当てオブジェクト (で作成new) である限り存続 -> 誰かがそれらへの参照を保持する限り存在

それに伴うもう 1 つの問題は、ガベージ コレクションが一定期間動作することです。オブジェクトがローカルの場合、関数が終了したらすぐにファイナライズする必要があります。これは、その後、メモリが他の変数によってオーバーライドされるためです。いずれにせよ、GC にオブジェクトのファイナライズを強制することはできません。

ただし、良いことは、C# JIT は、オブジェクトをスタックに安全に割り当てることができると判断できる場合があり (常にではありません)、可能な場合はスタック割り当てに頼ることです (これも時々)。

一方、C++ では、どこでもすべてを宣言できますが、これには C# や Java よりも安全性が低くなりますが、アプリケーションを微調整して高性能を実現することができます - 低リソース アプリケーション

于 2016-02-24T09:31:26.233 に答える
-1

ザナトスが正しい答えを投稿したと思います。

とにかく、これは答えではなく、別の答えの反例です。

次のコードを検討してください。

using System;
using System.Threading;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            doAsync();
            Thread.Sleep(2000);
            Console.WriteLine("Did we finish?"); // Likely this is never displayed.
        }

        public static unsafe void doAsync()
        {
            int n = 10000;
            int* arr = stackalloc int[n];
                ThreadPool.QueueUserWorkItem(x => {
                Thread.Sleep(1000);

                for (int i = 0; i < n; ++i)
                    arr[i] = 0;
            });
        }
    }
}

そのコードを実行すると、スタック メモリが解放された後にスタック配列が書き込まれるため、クラッシュします。

これは、参照型で stackalloc を使用できない理由が、単にこの種のエラーを防止するためではないことを示しています。

于 2016-02-24T09:53:28.763 に答える