将int[][]转换为int**的最佳方法是什么?

4

这是一个好奇的问题。我已经有一个现有的解决方案,但我想知道是否有更好的方法。

我的呼叫者想要使用int [][ ]来调用我。我有一个需要处理int **的例程。如何进行此转换?换句话说:

public static void Func1(int[][] data) {
  Func2(data); //how to do this?
}

private unsafe static void Func2(int** data) {
  //process data
}

以下是我能想到的最佳方法。它运行良好,但我不能说我对递归方式(和O(data.Length)堆栈空间的需求)感到100%满意。
public static void Main() {
  var test=new[] {
    new [] {10},
    new [] {20,30},
    new [] {40,50,60},
    new [] {70,80,90,100},
  };
  MySolution_Func1(test);
}

public unsafe static void MySolution_Func1(int[][] data) {
  var items=new int*[data.Length];
  Recurse(0, data, items);
}

public unsafe static void Recurse(int index, int[][] data, int*[] build) {
  if(index==data.Length) {
    fixed(int** finalp=build) {
      Func2(finalp);
    }
  } else {
    fixed(int* nextp=data[index]) {
      build[index]=nextp;
      Recurse(index+1, data, build);
    }
  }
}

private unsafe static void Func2(int** data) {
  for(var j=0; j<4; ++j) {
    for(var i=0; i<j+1; ++i) {
      Debug.WriteLine("{0},{1}: {2}", j, i, data[j][i]);
    }
  }
}

我不是要抢答,但是int**是什么? - James
一个指向指针的指针,类型为int。 - Ed S.
@James:一个指向整型变量的指针的指针:http://msdn.microsoft.com/zh-cn/library/y31yhkeb(v=vs.80).aspx - Tremmors
你可以移除递归,但是不规则的 int[][] 数组需要对每一行进行某种形式的迭代...最好在首次接受 int** 的函数中完成这个操作--也就是说,不要调整数据,而是调整使用方式。对于“一次性”情况更加实用。此外,考虑使用 int[,] 并将其固定到具有“虚拟索引”的 int* 上。祝编码愉快。 - user166390
如果您使用多维数组和不安全代码,那么您应该已经发现使用一维数组会更快。由于您甚至在访问数组之前分配内存以获取指针指针的存储空间,我敢打赌,如果您使用一维数组和一个接受int *的函数,您的速度至少会快2-10倍。 - Alois Kraus
1
感谢@Alois。我同意,最终我也这样做了。但是从“知识好奇心”的角度来看,我仍然对这个问题很感兴趣。 - Corey Kosak
3个回答

0
public unsafe void ConvertToNative(int[][] jarray, out int** ptr)
        {

            ptr= (int**)Marshal.AllocHGlobal(jarray.Length*sizeof(int));

            for (int i = 0; i < jarray.Length; i++)
            {
                *(ptr+i) = (int*)Marshal.AllocHGlobal(jarray[i].Length*sizeof(int));
                for (int j = 0; j < jarray[i].Length; j++)
                {
                    (*(i + ptr))[j] = jarray[i][j];
                }
            }
        }

这个可以工作,但使用了非托管内存,并且没有递归,这样合法吗?


有趣的是,虽然你需要复制交错数组的每个元素,但这有点可惜。 - Corey Kosak

0

没有必要复制整个数组。您可以创建一个指针数组(即IntPtr[]),然后将其转换为int**。这不是很好看,我不建议这样做。但它是可行的。下面的代码展示了如何实现。

int[][] myArray = new int[10][];
for (int i = 0; i < 10; ++i)
{
    int[] x = new int[10];
    for (int j = 0; j < 10; ++j)
    {
        x[j] = 10 * i + j;
    }
    myArray[i] = x;
}

// Output the array
Console.WriteLine("int[][]");
for (int i = 0; i < 10; ++i)
{
    for (int j = 0; j < 10; ++j)
    {
        Console.Write("{0}, ", myArray[i][j]);
    }
    Console.WriteLine();
}

// Convert to int*[]
unsafe
{
    GCHandle[] handles = new GCHandle[10];
    IntPtr[] ArrayOfPointer = new IntPtr[10];
    for (int i = 0; i < 10; ++i)
    {
        handles[i] = GCHandle.Alloc(myArray[i], GCHandleType.Pinned);
        ArrayOfPointer[i] = handles[i].AddrOfPinnedObject();
    }
    // Okay, let's output that
    Console.WriteLine("int*[]");
    for (int i = 0; i < 10; ++i)
    {
        int* p = (int*)ArrayOfPointer[i];
        for (int j = 0; j < 10; ++j)
        {
            Console.Write("{0}, ", *p);
            ++p;
        }
        Console.WriteLine();
    }

    // now convert to int**
    GCHandle bigHandle = GCHandle.Alloc(ArrayOfPointer, GCHandleType.Pinned);
    int** ppInt = (int**)bigHandle.AddrOfPinnedObject();

    // and output it
    Console.WriteLine("int**");
    int** pa = ppInt;
    for (int i = 0; i < 10; ++i)
    {
        int* p = *pa;
        for (int j = 0; j < 10; ++j)
        {
            Console.Write("{0}, ", *p);
            ++p;
        }
        Console.WriteLine();
        ++pa;
    }

    // Need to free the handles
    bigHandle.Free();
    for (int i = 0; i < 10; ++i)
    {
        handles[i].Free();
    }
}

哇,太酷了。你说得对,我可能不会使用这种方法,但学习新东西很有趣。 - Corey Kosak

0

如果你愿意将所有数据复制到另一个缓冲区中,你可以使用O(1)堆栈空间来完成:

public unsafe static void AlternateSolution_Func1(int[][] data) {
  var buffer=new int[data.Sum(a => a.Length)];
  fixed(int* pBuffer=buffer) {
    var items=new int*[data.Length];
    int count=0;
    for(int i=0; i<data.Length; ++i) {
      items[i]=pBuffer+count;
      var array=data[i];
      for(int j=0; j<array.Length; ++j) {
        pBuffer[count++]=array[j];
      }
    }
    fixed(int** pItems=items) {
      Func2(pItems);
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接