14

ここでの簡単な質問:ジャグ配列からダブルポインタに変換する方法はありますか?

double[][]例:に変換double**

残念ながら、これは(昔ながらのCのように)残念ながらキャストするだけでは実行できません。ステートメントを使用してfixedも問題は解決しないようです。C#でこれを達成するための(できれば可能な限り効率的な)方法はありますか?簡単な解決策を望んでいますが、解決策はまったく明白ではないかもしれません。

4

3 に答える 3

7

少し安全。
最初のソリューションへのコメントで述べたように、ネストされた配列は移動できるため、それらも固定する必要があります。

unsafe
{
    double[][] array = new double[3][];
    array[0] = new double[] { 1.25, 2.28, 3, 4 };
    array[1] = new double[] { 5, 6.24, 7.42, 8 };
    array[2] = new double[] { 9, 10.15, 11, 12.14 };

    GCHandle[] pinnedArray = new GCHandle[array.Length];
    double*[] ptrArray = new double*[array.Length];

    for (int i = 0; i < array.Length; i++)
    {
        pinnedArray[i] = GCHandle.Alloc(array[i], GCHandleType.Pinned);
    }

    for (int i = 0; i < array.Length; ++i)
    {
        // as you can see, this pointer will point to the first element of each array
        ptrArray[i] = (double*)pinnedArray[i].AddrOfPinnedObject();
    }

    // here is your double**
    fixed(double** doublePtr = &ptrArray[0])
    {
        Console.WriteLine(**doublePtr);
    }

    // unpin all the pinned objects,
    // otherwise they will live in memory till assembly unloading
    // even if they will went out of scope
    for (int i = 0; i < pinnedArray.Length; ++i)
        pinnedArray[i].Free();
}

問題の簡単な説明:

ヒープにいくつかのオブジェクトを割り当てると、ガベージコレクションでそれらを別の場所に移動できます。したがって、次の状況を想像してみてください。オブジェクトと内部配列を割り当てた場合、それらはすべてヒープ上でゼロ世代に配置されます。

ここに画像の説明を入力してください

現在、一部のオブジェクトはスコープから外れてガベージになり、一部のオブジェクトは割り当てられたばかりです。ガベージコレクターは、古いオブジェクトをヒープから移動し、他のオブジェクトを最初のオブジェクトに近づけたり、次世代に近づけたりして、ヒープを圧縮します。結果は次のようになります。

ここに画像の説明を入力してください

したがって、私たちの目標は、一部のオブジェクトをヒープに「固定」して、オブジェクトが移動しないようにすることです。この目標を達成するために何をしなければなりませんか?ステートメントとGCHandle.Allocateメソッドを修正しました。

まず、何をGCHandle.Allocateしますか?これは、パラメータとしてメソッドに渡されたオブジェクトへの参照を持つ内部システムテーブルに新しいエントリを作成します。したがって、ガベージコレクターがヒープを調べるとき、内部テーブルのエントリをチェックし、エントリが見つかった場合は、オブジェクトを有効としてマークし、ヒープから移動しません。次に、このオブジェクトがどのように固定されているかを確認し、圧縮段階でオブジェクトをメモリ内で移動しません。fixedステートメントは、スコープを離れるときにオブジェクトを自動的に「固定解除」することを除いて、ほとんど同じです。

要約:固定された各オブジェクトfixedは、スコープを離れると自動的に「固定解除」されます。私たちの場合、それはループの次の反復になります。

オブジェクトが移動されたりガベージコレクションされたりしないことを確認する方法:ゼロ生成のためにヒープの予算をすべて消費し、GCにヒープを圧縮させます。言い換えると、ヒープ上に多くのオブジェクトを作成します。そして、オブジェクトを固定または「修正」した後にそれを行います。

for(int i = 0; i < 1000000; ++i)
{
    MemoryStream stream = new MemoryStream(10);
    //make sure that JIT will not optimize anything, make some work
    stream.Write(new Byte[]{1,2,3}, 1, 2);
}
GC.Collect();

小さな注意:ヒープには、大きなオブジェクト用と小さなオブジェクト用の2種類があります。オブジェクトが大きい場合は、コードをチェックするために大きなオブジェクトを作成する必要があります。そうしないと、小さなオブジェクトによってGCがガベージコレクションと圧縮を開始することはありません。

最後に、いくつかのサンプルコードを示します。これは、固定されていない/固定されていないポインタを使用して基になる配列にアクセスすることの危険性を示しています。

namespace DangerousNamespace
{
    // WARNING!
    // This code includes possible memory access errors with unfixed/unpinned pointers!
    public class DangerousClass
    {
        public static void Main()
        {
            unsafe
            {
                double[][] array = new double[3][];
                array[0] = new double[] { 1.25, 2.28, 3, 4 };
                array[1] = new double[] { 5, 6.24, 7.42, 8 };
                array[2] = new double[] { 9, 10.15, 11, 12.14 };

                fixed (double* junk = &array[0][0])
                {
                    double*[] arrayofptr = new double*[array.Length];
                    for (int i = 0; i < array.Length; i++)
                        fixed (double* ptr = &array[i][0])
                        {
                            arrayofptr[i] = ptr;
                        }

                    for (int i = 0; i < 10000000; ++i)
                    {
                        Object z = new Object();
                    }
                    GC.Collect();

                    fixed (double** ptrptr = &arrayofptr[0])
                    {
                        for (int i = 0; i < 1000000; ++i)
                        {
                            using (MemoryStream z = new MemoryStream(200))
                            {
                                z.Write(new byte[] { 1, 2, 3 }, 1, 2);
                            }
                        }
                        GC.Collect();
                        // should print 1.25
                        Console.WriteLine(*(double*)(*(double**)ptrptr));
                    }
                }
            }
        }
    }
}
于 2015-12-09T17:30:21.420 に答える
4

double [][]はdouble*の配列であり、double *の配列ではないため、double **を取得するには、最初にdouble*[]が必要です。

double[][] array = //whatever
//initialize as necessary

fixed (double* junk = &array[0][0]){

    double*[] arrayofptr = new double*[array.Length];
    for (int i = 0; i < array.Length; i++)
        fixed (double* ptr = &array[i][0])
        {
            arrayofptr[i] = ptr;
        }

    fixed (double** ptrptr = &arrayofptr[0])
    {
        //whatever
    }
}

私はこれが何のためにあるのか、そしてダブルポインターを必要とするよりも良い解決策があるのか​​どうか疑問に思わずにはいられません。

于 2009-05-20T21:06:36.033 に答える
-5

私は当分の間zachrrsソリューションを使用しました(これは、最初に実行する必要があるかもしれないと私が思っていたものでした)。これが拡張メソッドです。

public static double** ToPointer(this double[][] array)
{
    fixed (double* arrayPtr = array[0])
    {
        double*[] ptrArray = new double*[array.Length];
        for (int i = 0; i < array.Length; i++)
        {
            fixed (double* ptr = array[i])
                ptrArray[i] = ptr;
        }

        fixed (double** ptr = ptrArray)
            return ptr;
    }
}
于 2009-05-20T21:42:03.823 に答える