代码之家  ›  专栏  ›  技术社区  ›  Noldorin

C语言中从交错数组到双指针的转换#

  •  13
  • Noldorin  · 技术社区  · 16 年前

    这里有一个简单的问题:有没有办法将锯齿状数组转换为双指针?

    e、 g.转换为 double[][] double**

    不幸的是,这不能仅仅通过铸造来实现(就像在普通的旧C中一样)。使用 fixed 声明似乎也不能解决问题。有没有(最好是尽可能有效的)方法在C#中实现这一点?我怀疑解决方案可能一点也不明显,尽管我还是希望有一个简单明了的解决方案。

    3 回复  |  直到 16 年前
        1
  •  7
  •   Shmil The Cat    6 年前

    有点安全。

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

    对问题的简要说明:

    当我们在堆上分配一些对象时,它们可以被移动到垃圾收集上的另一个位置。所以,想象下一种情况:您已经分配了一些对象和内部数组,它们都被放置在堆的零代中。

    enter image description here

    enter image description here

    所以,我们的目标是将一些对象固定在堆中,这样它们就不会移动。 要实现这一目标,我们必须做些什么?我们有 fixed 声明及 GCHandle.Allocate

    首先,什么 GCHandle.Allocate 做它在内部系统表中创建一个新条目,该条目引用作为参数传递给方法的对象。所以,当垃圾收集器检查堆时,他会检查内部表中的条目,如果他找到条目,他会将对象标记为活动的,并且不会将其移出堆。然后,他将查看此对象是如何固定的,并且在压缩阶段不会移动内存中的对象。 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();
    

    小提示:有两种类型的堆–大对象堆和小对象堆。如果对象很大,则应该创建大对象来检查代码,否则小对象不会强制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));
                        }
                    }
                }
            }
        }
    }
    
        2
  •  4
  •   bsneeze    16 年前

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

        3
  •  -5
  •   Noldorin    16 年前

    我暂时采用了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;
        }
    }
    
    推荐文章