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

到目前为止,我有这个:

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

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

double anArray[10][10];

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

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


当前回答

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

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[][]是一个包含字符的内存块。这对编译器如何翻译代码以及最终的性能会产生影响。

其他回答

我们可以使用几种方法将2D数组传递给函数:

Using single pointer we have to typecast the 2D array. #include<bits/stdc++.h> using namespace std; void func(int *arr, int m, int n) { for (int i=0; i<m; i++) { for (int j=0; j<n; j++) { cout<<*((arr+i*n) + j)<<" "; } cout<<endl; } } int main() { int m = 3, n = 3; int arr[m][n] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; func((int *)arr, m, n); return 0; } Using double pointer In this way, we also typecast the 2d array #include<bits/stdc++.h> using namespace std; void func(int **arr, int row, int col) { for (int i=0; i<row; i++) { for(int j=0 ; j<col; j++) { cout<<arr[i][j]<<" "; } printf("\n"); } } int main() { int row, colum; cin>>row>>colum; int** arr = new int*[row]; for(int i=0; i<row; i++) { arr[i] = new int[colum]; } for(int i=0; i<row; i++) { for(int j=0; j<colum; j++) { cin>>arr[i][j]; } } func(arr, row, colum); 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>>,或者用户定义的类型以最大限度地重用代码。

你可以像这样创建一个函数模板:

template<int R, int C>
void myFunction(double (&myArray)[R][C])
{
    myArray[x][y] = 5;
    etc...
}

然后你通过R和c有两个维度大小,每个数组大小都会创建一个不同的函数,所以如果你的函数很大,并且你用各种不同的数组大小调用它,这可能代价很高。你可以像这样使用它作为一个函数的包装器:

void myFunction(double * arr, int R, int C)
{
    arr[x * C + y] = 5;
    etc...
}

它将数组视为一维,并使用算术计算出索引的偏移量。在这种情况下,你可以这样定义模板:

template<int C, int R>
void myFunction(double (&myArray)[R][C])
{
    myFunction(*myArray, R, C);
}

将二维数组传递给函数有三种方法:

参数为2D数组 int数组[10][10]; void passFunc(int a[][10]) { / /…… } passFunc(数组); 形参是一个包含指针的数组 int数组* [10]; For (int I = 0;I < 10;我+ +) 数组[i] = new int[10]; void passFunc(int *a[10]) //指针数组 { / /…… } passFunc(数组); 形参是指向指针的指针 int * *数组; 数组= new int *[10]; For (int I = 0;我< 10;我+ +) 数组[i] = new int[10]; void passFunc(int **a) { / /…… } passFunc(数组);

固定大小

1. 通过引用传递

template <size_t rows, size_t cols>
void process_2d_array_template(int (&array)[rows][cols])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

在c++中,通过引用传递数组而不丢失维度信息可能是最安全的,因为不需要担心调用者传递不正确的维度(当不匹配时编译器会标记)。然而,这对于动态(独立式)数组是不可能的;它只适用于自动(通常是栈生存的)数组,即维度应该在编译时知道。

2. 传递指针

void process_2d_array_pointer(int (*array)[5][10])
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < 5; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << (*array)[i][j] << '\t';
        std::cout << std::endl;
    }    
}

The C equivalent of the previous method is passing the array by pointer. This should not be confused with passing by the array's decayed pointer type (3), which is the common, popular method, albeit less safe than this one but more flexible. Like (1), use this method when all the dimensions of the array is fixed and known at compile-time. Note that when calling the function the array's address should be passed process_2d_array_pointer(&a) and not the address of the first element by decay process_2d_array_pointer(a).

变量的大小

这些维继承自C语言,但不太安全,编译器没有办法检查,确保调用者传递了所需的维。该函数仅依赖调用者传入的维度。它们比上面的更灵活,因为不同长度的数组可以不变地传递给它们。

需要记住的是,在C中没有直接将数组传递给函数这样的事情[而在c++中,它们可以作为引用传递(1)];(2)将指针传递给数组,而不是数组本身。始终按原样传递数组成为一个指针复制操作,这得益于数组衰减为指针的特性。

3.传递一个指向衰减类型的指针(value)

// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < 10; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Although int array[][10] is allowed, I'd not recommend it over the above syntax since the above syntax makes it clear that the identifier array is a single pointer to an array of 10 integers, while this syntax looks like it's a 2D array but is the same pointer to an array of 10 integers. Here we know the number of elements in a single row (i.e. the column size, 10 here) but the number of rows is unknown and hence to be passed as an argument. In this case there's some safety since the compiler can flag when a pointer to an array with second dimension not equal to 10 is passed. The first dimension is the varying part and can be omitted. See here for the rationale on why only the first dimension is allowed to be omitted.

4. 将一个指针传递给另一个指针

// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
    std::cout << __func__ << std::endl;
    for (size_t i = 0; i < rows; ++i)
    {
        std::cout << i << ": ";
        for (size_t j = 0; j < cols; ++j)
            std::cout << array[i][j] << '\t';
        std::cout << std::endl;
    }
}

Again there's an alternative syntax of int *array[10] which is the same as int **array. In this syntax the [10] is ignored as it decays into a pointer thereby becoming int **array. Perhaps it is just a cue to the caller that the passed array should have at least 10 columns, even then row count is required. In any case the compiler doesn't flag for any length/size violations (it only checks if the type passed is a pointer to pointer), hence requiring both row and column counts as parameter makes sense here.

Note: (4) is the least safest option since it hardly has any type check and the most inconvenient. One cannot legitimately pass a 2D array to this function; C-FAQ condemns the usual workaround of doing int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10); as it may potentially lead to undefined behaviour due to array flattening. The right way of passing an array in this method brings us to the inconvenient part i.e. we need an additional (surrogate) array of pointers with each of its element pointing to the respective row of the actual, to-be-passed array; this surrogate is then passed to the function (see below); all this for getting the same job done as the above methods which are more safer, cleaner and perhaps faster.

下面是一个测试上述功能的驱动程序:

#include <iostream>

// copy above functions here

int main()
{
    int a[5][10] = { { } };
    process_2d_array_template(a);
    process_2d_array_pointer(&a);    // <-- notice the unusual usage of addressof (&) operator on an array
    process_2d_array(a, 5);
    // works since a's first dimension decays into a pointer thereby becoming int (*)[10]

    int *b[5];  // surrogate
    for (size_t i = 0; i < 5; ++i)
    {
        b[i] = a[i];
    }
    // another popular way to define b: here the 2D arrays dims may be non-const, runtime var
    // int **b = new int*[5];
    // for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
    process_pointer_2_pointer(b, 5, 10);
    // process_2d_array(b, 5);
    // doesn't work since b's first dimension decays into a pointer thereby becoming int**
}