如何将 C# 中的二维数组传递给 C++ DLL?

Posted

技术标签:

【中文标题】如何将 C# 中的二维数组传递给 C++ DLL?【英文标题】:How to pass a two-dimensional array in C# to a C++ DLL? 【发布时间】:2020-04-15 03:21:18 【问题描述】:

我想将 C# 中的二维数组作为参数传递给 C++ DLL 中的函数(它必须是 C++,因为我使用的是 CUDA C++)。我尝试了很多东西,但未能使用数组或向量直接传递它。我唯一能做的就是将其转换为一维数组,将其连同其维度一起传递给函数,然后将其转换回二维向量。 这是 C++ DLL 代码:

int Add(int* a, int m, int n)

    int i, j, ans;
    vector<vector<int>> A(m, vector<int>(n));
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            A[i][j] = a[i * n + j];
    // Main calculations
    return ans;

这是传递数组的 C# 代码:

[DllImport("CUDALib.dll")]
static extern int Add(int[] a, int m, int n);
private void PassArray(int[,] A)

    int i, j, m, n, ans;
    int[] a;
    m = A.GetLength(0);
    n = A.GetLength(1);
    a = new int[m * n];
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            a[i * n + j] = A[i, j];
    ans = Add(a, m, n);

我有什么更快、更有效、更直接的方法吗?

【问题讨论】:

以一维数组的形式传递既好又高效,但将其转换为vector&lt;vector&lt;T&gt;&gt; 会产生大量的堆分配和取消分配。将其保持为 1D 形式,并创建一个您可以使用 2 维访问的类,如果您需要清晰的话。 “可以二维访问的类”是什么意思? 我发布了一个答案,展示了一种非常有效的方法,它使用标准的[n][m] 方法来访问数组元素。 【参考方案1】:

C# 中的二维数组是contiguous in memory,因此不需要在任何一侧复制所有内存。您应该按原样在 C# 中传递数组指针:

[DllImport("CUDALib.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int Add(int[,] a, int m, int n);
static void PassArray(int[,] A)

    int m = A.GetLength(0);
    int n = A.GetLength(1);
    int ans = Add(A, m, n);

然后像这样访问 C++ 中的各个元素:

extern "C" __declspec(dllexport) int Add(int* a, int m, int n)

    int i, j;
    for (i = 0; i < m; i++)
        for (j = 0; j < n; j++)
            printf("%d %d: %d\n", i, j, a[i * n + j]);
    // Main calculations
    return 0;

【讨论】:

你解决了我问题的前半部分——通过了——但是有没有希望我可以在 C++ 中接收数组作为二维数组,因为这将更适合我的计算,矩阵乘法, 逆等。 如果你真的需要它作为二维数组运行,你可以使用类似这样的东西:***.com/a/22288609/4454124 几乎没有性能损失 你就是男人!【参考方案2】:

您可以使用一个类来封装指针、行和列,然后访问 传递一维数组,就好像它是一个标准的二维数组一样。由于 p 通过调用保持有效,因此无需在函数内制作本地副本。 @mnistic 的回答还指出,无需在 C# 中制作一维副本。

class Array2D 
public:
    Array2D(int* p, size_t rows, size_t cols) : p(p), cols(cols), rows(rows);
    int* operator[](int row)  return p + row * cols; 
    const size_t rows;  // not needed but can be used for adding boundary checks
    const size_t cols;
private:
    int *const p;
;

返回以二维数组表示的数组元素的总和

extern "C" __declspec(dllexport) int Add(int* p, int rows, int cols)

    Array2D a(p, rows, cols);
    int sum;
    for (int i = 0; i < rows; ++i)
        for (int ii = 0; ii < cols; ++ii)
            sum += a[i][ii];
    return sum;

这是一个展示其工作原理的测试

int main()

    int* p = new int[6] 1, 2, 3, 4, 5, 6 ;
    Array2D a2d(p, 3, 2);   // three rows, two cols
    std::cout << a2d[0][0] << '\n';  // prints 1
    std::cout << a2d[1][1] << '\n';  // prints 4
    a2d[1][1] = 10;
    std::cout << a2d[1][1] << '\n';  // now prints 10
    std::cout << a2d[2][0] << '\n';  // prints 5
    std::cout << a2d[2][1] << '\n';  // prints 6

【讨论】:

我最终把它做成了一个模板类。【参考方案3】:

这是 AbdelAziz 回答的模板类:

    template <class TdataType> 
class Array2d 
public:
    Array2d<TdataType>(TdataType* arr, size_t rows, size_t cols) : p(arr), cols(cols), rows(rows) ;
    TdataType* operator[](int row)  return p + row * cols; 
    const size_t rows;  // not needed but can be used for adding boundary checks
    const size_t cols;
private:
    TdataType* const p;
;

这是一个使用它的例子:

auto my2dArray = Array2d<uint8_t>(inputArrayFromCsharp, numRows, numCols);

【讨论】:

以上是关于如何将 C# 中的二维数组传递给 C++ DLL?的主要内容,如果未能解决你的问题,请参考以下文章

如何将 uint[] c# 传递给 c++ dll?

如何在托管 (C#) 和非托管 (C++) 代码之间来回传递数组内容

将 c++ dll 的 char 指针数组传递给 c# 字符串

如何将二维数组从 C# 传递到 C++?

如何将 COM 对象的 C# 引用传递给 C++ DLL

将 C# 字符串数组传递给 C++