我有一个函数,我想把它作为一个参数,一个可变大小的二维数组。

到目前为止,我有这个:

void myFunction(double** myArray){
     myArray[x][y] = 5;
     etc...
}

我在代码的其他地方声明了一个数组:

double anArray[10][10];

然而,调用myFunction(anArray)会给我一个错误。

我不想在传入数组时复制它。在myFunction中所做的任何更改都应该改变anArray的状态。如果我理解正确,我只想传递一个指向2D数组的指针作为参数。该函数还需要接受不同大小的数组。比如[10][10][5][5]。我该怎么做呢?


当前回答

尽管表面上看,double**隐含的数据结构与固定c数组(double[][])的数据结构根本不兼容。 问题是这两种方法都是C(或c++)中处理数组的流行(尽管)被误导的方法。 参见https://www.fftw.org/fftw3_doc/Dynamic-Arrays-in-C_002dThe-Wrong-Way.html

如果你不能控制代码的任何一部分,你需要一个翻译层(这里称为adapt),解释在这里:https://c-faq.com/aryptr/dynmuldimary.html

您需要生成一个指针的辅助数组,指向c数组的每一行。

#include<algorithm>
#include<cassert>
#include<vector>

void myFunction(double** myArray) {
    myArray[2][3] = 5;
}

template<std::size_t N, std::size_t M>
auto adapt(double(&Carr2D)[N][M]) {
    std::array<double*, N> ret;
    std::transform(
        std::begin(Carr2D), std::end(Carr2D),
        ret.begin(),
        [](auto&& row) { return &row[0];}
    );
    return ret;
}

int main() {
    double anArray[10][10];

    myFunction( adapt(anArray).data() );

    assert(anArray[2][3] == 5);
}

(参见工作代码:https://godbolt.org/z/7M7KPzbWY)

如果它看起来像是一场灾难,那是因为,正如我所说,这两种数据结构从根本上是不兼容的。


如果可以控制代码的两端,那么现在最好使用现代(或半现代)数组库,比如Boost。MultiArray,提振。uBLAS,特征或多。 如果数组要小,你有“小”数组库,例如在Eigen内,或者如果你不能负担任何依赖,你可以尝试简单的std::array<std::array<double, N>, M>。

使用Multi,你可以简单地这样做:

#include<multi/array.hpp>

#include<cassert>

namespace multi = boost::multi;

template<class Array2D>
void myFunction(Array2D&& myArray) {
    myArray[2][3] = 5;
}

int main() {
    multi::array<double, 2> anArray({10, 10});

    myFunction(anArray);

    assert(anArray[2][3] == 5);
}

(工作代码:https://godbolt.org/z/7M7KPzbWY)

其他回答

如果你想将int的[2][3]传递给void func(int** pp),你需要以下辅助步骤。

int a[2][3];
int* p[2] = {a[0],a[1]};
int** pp = p;

func(pp);

由于第一个[2]可以隐式指定,因此可以进一步简化为。

int a[][3];
int* p[] = {a[0],a[1]};
int** pp = p;

func(pp);

你可以省略最左边的维度,所以你最终有两个选项:

void f1(double a[][2][3]) { ... }

void f2(double (*a)[2][3]) { ... }

double a[1][2][3];

f1(a); // ok
f2(a); // ok 

指针也是如此:

// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double***’ 
// double ***p1 = a;

// compilation error: cannot convert ‘double (*)[2][3]’ to ‘double (**)[3]’
// double (**p2)[3] = a;

double (*p3)[2][3] = a; // ok

// compilation error: array of pointers != pointer to array
// double *p4[2][3] = a;

double (*p5)[3] = a[0]; // ok

double *p6 = a[0][1]; // ok

c++标准允许将N维数组衰减为指向N-1维数组的指针,因为您可以丢失最左边的维度,但仍然能够正确访问具有N-1维信息的数组元素。

详情在这里

但是,数组和指针是不一样的:数组可以衰减为指针,但是指针不携带关于它所指向的数据的大小/配置的状态。

char **是指向包含字符指针的内存块的指针,这些字符指针本身指向字符的内存块。char[][]是一个包含字符的内存块。这对编译器如何翻译代码以及最终的性能会产生影响。

传递多维数组的一个重要的事情是:

第一个数组维度不需要指定。 必须指定第二个维度(任何其他维度)。

1.当全局只有第二个维度可用时(作为宏或全局常量)

const int N = 3;

void print(int arr[][N], int m)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < N; j++)
    printf("%d ", arr[i][j]);
}

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
print(arr, 3);
return 0;
}

2.使用单个指针: 在此方法中,当传递给函数时,必须对2D数组进行类型转换。

void print(int *arr, int m, int n)
{
int i, j;
for (i = 0; i < m; i++)
  for (j = 0; j < n; j++)
    printf("%d ", *((arr+i*n) + j));
 }

int main()
{
int arr[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int m = 3, n = 3;

// We can also use "print(&arr[0][0], m, n);"
print((int *)arr, m, n);
return 0;
}

令人惊讶的是还没有人提到这一点,但是您可以简单地在任何支持[][]语义的2D上创建模板。

template <typename TwoD>
void myFunction(TwoD& myArray){
     myArray[x][y] = 5;
     etc...
}

// call with
double anArray[10][10];
myFunction(anArray);

它适用于任何2D“类数组”数据结构,例如std::vector<std::vector<T>>,或者用户定义的类型以最大限度地重用代码。

你可以这样做……

#include<iostream>

using namespace std;

//for changing values in 2D array
void myFunc(double *a,int rows,int cols){
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            *(a+ i*rows + j)+=10.0;
        }
    }
}

//for printing 2D array,similar to myFunc
void printArray(double *a,int rows,int cols){
    cout<<"Printing your array...\n";
    for(int i=0;i<rows;i++){
        for(int j=0;j<cols;j++){
            cout<<*(a+ i*rows + j)<<"  ";
        }
    cout<<"\n";
    }
}

int main(){
    //declare and initialize your array
    double a[2][2]={{1.5 , 2.5},{3.5 , 4.5}};

    //the 1st argument is the address of the first row i.e
    //the first 1D array
    //the 2nd argument is the no of rows of your array
    //the 3rd argument is the no of columns of your array
    myFunc(a[0],2,2);

    //same way as myFunc
    printArray(a[0],2,2);

    return 0;
}

你的输出将如下…

11.5  12.5
13.5  14.5