将2D数组传递给c++函数

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

到目前为止,我有这个:

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

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

double anArray[10][10];

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

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

959140 次浏览

anArray[10][10]不是指向指针的指针,它是一个连续的内存块,适合存储100个double类型的值,编译器知道如何寻址,因为你指定了维度。您需要将它作为数组传递给函数。你可以省略初始维度的大小,如下所示:

void f(double p[][10]) {
}

但是,这将不允许您传递除10之外的最后一个维度的数组。

c++中最好的解决方案是使用std::vector<std::vector<double> >:它几乎同样高效,而且明显更方便。

一维数组衰减为指向数组中第一个元素的指针。而2D数组则衰减为指向第一行的指针。所以,函数原型应该是-

void myFunction(double (*myArray) [10]);

我更喜欢std::vector而不是原始数组。

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

  1. 2D数组

    int array[10][10];
    void passFunc(int a[][10])
    {
    // ...
    }
    passFunc(array);
    
  2. The parameter is an array containing pointers

    int *array[10];
    for(int i = 0; i < 10; i++)
    array[i] = new int[10];
    void passFunc(int *a[10]) //Array containing pointers
    {
    // ...
    }
    passFunc(array);
    
  3. The parameter is a pointer to a pointer

    int **array;
    array = new int *[10];
    for(int i = 0; i <10; i++)
    array[i] = new int[10];
    void passFunc(int **a)
    {
    // ...
    }
    passFunc(array);
    

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

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

对shengy第一个建议的修改,你可以使用模板让函数接受多维数组变量(而不是存储一个必须被管理和删除的指针数组):

template <size_t size_x, size_t size_y>
void func(double (&arr)[size_x][size_y])
{
printf("%p\n", &arr);
}


int main()
{
double a1[10][10];
double a2[5][5];


printf("%p\n%p\n\n", &a1, &a2);
func(a1);
func(a2);


return 0;
}

打印语句用于显示数组是通过引用传递的(通过显示变量的地址)

固定大小

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

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

前面方法的C等效方法是通过指针传递数组。这不应该与传递数组的衰减指针类型(3)相混淆,后者是常见的、流行的方法,尽管没有这个方法安全,但更灵活。像(1)一样,当数组的所有尺寸都是固定的并且在编译时已知时,使用此方法。注意,当调用函数时,数组的地址应该被传递process_2d_array_pointer(&a),而不是通过衰变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;
}
}

虽然允许使用int array[][10],但我不建议使用上面的语法,因为上面的语法清楚地表明标识符array是指向10个整数数组的单个指针,而这个语法看起来就像一个2D数组,但它是指向10个整数数组的相同指针。这里我们知道单行中的元素数量(即列大小,这里是10),但行数未知,因此将作为参数传递。在这种情况下,有一些安全性,因为编译器可以在传递一个指向二维不等于10的数组的指针时进行标记。第一个维度是变化部分,可以省略。这里有基本原理关于为什么只允许省略第一个维度。

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

同样,还有另一种语法int *array[10],与int **array相同。在这种语法中,[10]会被忽略,因为它会衰减为指针,从而变成int **array。也许这只是给调用者一个提示,即传递的数组应该至少有10列,即使这样也需要行数。在任何情况下,编译器都不会标记任何长度/大小违规(它只检查传递的类型是否为指针到指针),因此要求行数和列数都作为参数在这里是有意义的。

注意: (4)是最不安全的选择,因为它几乎没有任何类型检查和最不方便。我们不能合法地将2D数组传递给这个函数;C-FAQ谴责通常的解决方法是执行int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);,因为它由于数组扁平而可能会导致未定义的行为。在这个方法中传递数组的正确方式给我们带来了不方便的部分,即我们需要一个额外的(代理)指针数组,其中的每个元素都指向实际要传递的数组的相应行;然后这个代理被传递给函数(见下文);所有这些都是为了完成与上述方法相同的工作,这些方法更安全,更清洁,也许更快。

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

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

你可以这样做……

#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

令人惊讶的是还没有人提到这一点,但是您可以简单地在任何支持[][]语义的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>>,或用户定义的类型,以最大限度地重用代码。

你可以使用c++中的模板功能来实现这一点。我是这样做的:

template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}
这种方法的问题是,对于你提供的每一个col值,一个新的函数定义都会使用模板实例化。 < / p >
int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);

实例化模板两次以生成2个函数定义(其中一个col = 3,另一个col = 5)。

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

  • First array dimension不需要指定。
  • 必须指定Second(any any further)dimension

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;
}
< p > 2.使用单个指针: 在此方法中,当传递给function时,必须对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数组传递给函数:

  • 使用单指针我们必须对2D数组进行类型转换。

     #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;
    }
    
  • 通过这种方式,我们也对2d数组进行了类型转换

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

这是一个向量中的向量矩阵的例子

#include <iostream>
#include <vector>
using namespace std;


typedef vector< vector<int> > Matrix;


void print(Matrix& m)
{
int M=m.size();
int N=m[0].size();
for(int i=0; i<M; i++) {
for(int j=0; j<N; j++)
cout << m[i][j] << " ";
cout << endl;
}
cout << endl;
}




int main()
{
Matrix m = { {1,2,3,4},
{5,6,7,8},
{9,1,2,3} };
print(m);


//To initialize a 3 x 4 matrix with 0:
Matrix n( 3,vector<int>(4,0));
print(n);
return 0;
}

输出:

1 2 3 4
5 6 7 8
9 1 2 3


0 0 0 0
0 0 0 0
0 0 0 0

如果你想将int a[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);

如果你想将一个动态大小的2-d数组传递给一个函数,使用一些指针可能对你有用。

void func1(int *arr, int n, int m){
...
int i_j_the_element = arr[i * m + j];  // use the idiom of i * m + j for arr[i][j]
...
}


void func2(){
...
int arr[n][m];
...
func1(&(arr[0][0]), n, m);
}

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

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

#include <iostream>


using std::cout;


template <typename table>
void PrintArray(table& a) {
const size_t rows = sizeof(a) / sizeof(a[0]);
const size_t cols = sizeof(a[0]) / sizeof(a[0][0]);
cout << "Array has " << rows << " rows and " << cols << " columns.\n\n";
for (size_t i = 0; i < rows; i++) {
for (size_t j = 0; j < cols; j++) {
cout << a[i][j] << ' ';
}
cout << '\n';
}
}


int main()
{
size_t a[5][8]{}; // you can change the type and size of the 2D array
PrintArray(a);
return 0;
}