문제

간단한 질문 여기 : 들쭉날쭉 한 배열에서 이중 포인터로 변환 할 수있는 방법이 있습니까?

EG 변환 a double[][] 에게 double**

불행히도 (평범한 C에서 가능한 것처럼) 불행히도 이것은 캐스팅하는 것만으로도 할 수 없습니다. 사용 a fixed 진술도 문제를 해결하지 못하는 것 같습니다. c#에서 이것을 달성 할 수있는 방법이 있습니까? 그럼에도 불구하고 솔루션이 전혀 분명하지 않을 수도 있지만, 그럼에도 불구하고 솔루션이 직접적으로 희망합니다.

도움이 되었습니까?

해결책

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
    }
}

나는 이것이 무엇인지 궁금해 할 수 없으며 더블 포인터를 요구하는 것보다 더 나은 솔루션이 있는지 궁금합니다.

다른 팁

약간의 안전.
첫 번째 솔루션에 대한 의견에 언급했듯이 중첩 어레이를 이동할 수 있으므로 고정해야합니다.

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();
}

문제에 대한 간단한 설명 :

힙에 일부 물체를 할당하면 쓰레기 수집의 다른 위치로 이동할 수 있습니다. 따라서 다음 상황을 상상해보십시오. 일부 물체와 내부 배열을 할당했으며 모두 힙에 0 세대에 배치됩니다.

enter image description here

이제 일부 물체는 범위에서 나와 쓰레기가되었으며 일부 물체는 방금 할당되었습니다. 쓰레기 수집가는 오래된 물체를 힙에서 옮기고 다른 물체를 시작 또는 차세대에 더 가깝게 옮기고 힙을 압축합니다. 결과는 다음과 같습니다.

enter image description here

따라서 우리의 목표는 힙의 일부 물체를 "고정"하는 것입니다. 이 목표를 달성하기 위해 무엇을해야합니까? 우리는 가지고 있습니다 결정된 진술 및 gchandle.ALLOGATE 방법.

먼저, 무엇을 GCHandle.Allocate 하다? 매개 변수로 메소드로 전달 된 객체를 참조하는 내부 시스템 테이블에서 새 항목을 만듭니다. 따라서 쓰레기 수집가가 힙을 검사 할 때, 그는 내부 테이블에 항목을 확인하고, 그가 하나를 찾을 때, 그는 물체를 살아있는 것으로 표시하고 그것을 힙에서 옮기지 않을 것입니다. 그런 다음 그는이 객체가 고정 된 방법을보고 압축 단계에서 메모리에서 객체를 움직이지 않을 것입니다. fixed 문은 스코프를 떠날 때 "UNPINS"객체를 자동으로 제외하고는 거의 동일합니다.

요약 : 고정 된 각 객체 fixed 그가 스코프를 떠나면 자동으로 "innipned"되지 않습니다. 우리의 경우, 다음 번 루프 반복에있을 것입니다.

객체가 이동하거나 쓰레기를 수집하지 않는지 확인하는 방법 : 제로 세대에 대한 모든 힙 예산을 소비하고 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();

작은 통지 : 큰 물체와 작은 물체에는 두 가지 유형의 힙이 있습니다. 객체가 크면 코드를 확인하기 위해 큰 개체를 만들어야합니다. 그렇지 않으면 작은 물체가 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));
                    }
                }
            }
        }
    }
}

나는 당분간 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;
    }
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top