将2D数组传递给C ++函数
我有一个函数,我想作为参数,可变大小的二维数组。
到目前为止,我有这样的:
void myFunction(double** myArray){ myArray[x][y] = 5; etc... }
我已经在我的代码中的其他地方声明了一个数组:
double anArray[10][10];
但是,调用myFunction(anArray)
给我一个错误。
我不想在传入数组时复制数组。在myFunction
所做的任何更改都会改变anArray
的状态。 如果我理解正确,我只想传入一个指向二维数组的指针。 该function也需要接受不同大小的数组。 例如, [10][10]
和[5][5]
。 我该怎么做?
有三种方法可以将2D数组传递给函数:
-
该参数是一个二维数组
int array[10][10]; void passFunc(int a[][10]) { // ... } passFunc(array);
-
该参数是一个包含指针的数组
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);
-
该参数是一个指向指针的指针
int **array; array = new int *[10]; for(int i = 0; i <10; i++) array[i] = new int[10]; void passFunc(int **a) { // ... } passFunc(array);
固定大小
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 ++中通过引用传递数组而不会丢失维度信息可能是最安全的,因为不必担心调用者传递了不正确的维度(编译器标志不匹配时)。 但是,这对于dynamic(freestore)数组是不可能的。 它只适用于自动( 通常是堆栈生命 )数组,即在编译时应该知道维数。
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等价物是通过指针传递数组。 这不应该与传递数组的衰败的指针types(3)相混淆,这是常用的stream行方法,尽pipe这个方法不如这个方法安全,但是更灵活。 像(1)一样,当数组的所有维度在编译时是固定的和已知的时使用这个方法。 请注意,在调用函数时,应该传递数组的地址process_2d_array_pointer(&a)
而不是通过衰减process_2d_array_pointer(a)
的第一个元素的地址。
可变大小
这些都是从Cinheritance的,但不太安全,编译器没有办法检查,保证调用者正在传递所需的维度。 该函数仅在调用者传入的维度上进行存储。 这些比上面的更灵活,因为不同长度的数组可以不变地传递给它们。
需要记住的是,不存在将数组直接传递给C中的函数的function[在C ++中它们可以作为参考(1) ]传递; (2)传递一个指向数组的指针而不是数组本身。 总是按原样传递一个数组变成了一个指针复制操作,这个操作是由数组的性质衰减为一个指针来实现的 。
3.传递(value)一个指向衰减types的指针
// 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; } }
尽pipeint array[][10]
是允许的,但我不推荐使用上面的语法,因为上面的语法清楚地表明了标识符array
是一个指向10个整数数组的单个指针,而这个语法看起来像是一个2D数组,但与10个整数的数组是相同的指针。 这里我们知道一行中的元素数量(即列大小,这里是10),但是行数是未知的,因此可以作为parameter passing。 在这种情况下,由于编译器可以标记何时传递了第二维不等于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
。 也许这对调用者来说只是一个提示,即通过的数组至less应该有10列,即使那么行数也是需要的。 在任何情况下,编译器都不会标记任何长度/大小违规(它只检查传递的types是否是指向指针的指针),因此在这里需要行和列计数作为参数。
注: (4)是最不安全的select,因为它几乎没有任何types的检查和最不方便的。 一个人不能合法地将二维数组传递给这个函数; C-FAQ谴责通常的做int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
解决方法int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
因为它可能会导致由于数组展平而导致的未定义行为 。 在这个方法中传递数组的正确方式将我们带到了不方便的部分,也就是说我们需要一个额外的(代理)指针数组,每个指向数组的元素指向实际的待传递数组的相应行。 然后这个代理被传递给函数(见下文)。 所有这些都是为了完成与上述更安全,更清洁和更快的方法相同的工作。
这里有一个驱动程序来testing上述function:
#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** }
shengy的第一个build议的修改,你可以使用模板来使函数接受一个multidimensional arrayvariables(而不是存储必须被pipe理和删除的指针数组):
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; }
打印语句表示数组正在通过引用传递(通过显示variables的地址)
你可以像这样创build一个函数模板:
template<int R, int C> void myFunction(double (&myArray)[R][C]) { myArray[x][y] = 5; etc... }
然后,通过R和C两个尺寸大小。将为每个数组大小创build一个不同的函数,所以如果你的函数很大,并且你用各种不同的数组大小调用它,这可能是昂贵的。 你可以使用它作为这样一个函数的包装:
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); }
anArray[10][10]
不是一个指向指针的指针,它是一个连续的内存块,适合存储100个doubletypes的值,由于指定了维度,因此编译器知道如何处理。 你需要把它作为一个数组传递给一个函数。 您可以省略初始维度的大小,如下所示:
void f(double p[][10]) { }
但是,这不会让你通过十个以外的最后一个维度的数组。
在C ++中最好的解决scheme是使用std::vector<std::vector<double> >
:它几乎同样高效,而且更方便。
一维数组衰减到指向数组中第一个元素的指针指针。 2Darrays衰减到指向第一行的指针。 所以,函数原型应该是 –
void myFunction(double (*myArray) [10]);
我更喜欢原始数组的std::vector
。
你可以做这样的事情…
#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 ie //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);
它可以与任何二维“类似数组”的数据结构一起工作,如std::vector<std::vector<T>>
,或用户定义的types以最大化代码重用。
您可以在C ++中使用模板工具来执行此操作。 我做了这样的事情:
template<typename T, size_t col> T process(T a[][col], size_t row) { ... }
这种方法的问题在于,对于您提供的每一个col值,都会使用该模板实例化一个新的函数定义。 所以,
int some_mat[3][3], another_mat[4,5]; process(some_mat, 3); process(another_mat, 4);
实例化模板两次以产生2个函数定义(其中col = 3和col = 5)。
传递multidimensional array的一个重要的事情是:
-
First array dimension
不需要指定。 -
Second(any any further)dimension
必须指定。
当只有第二维在全球范围内可用时(无论是作为macros还是作为全局常量)
`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.使用单个指针 :在这个方法中,我们必须在传递函数时对二维数组进行types转换。
`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数组传递给函数:
-
使用单个指针我们必须对二维数组进行types化。
#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; }
-
使用双指针通过这种方式,我们还可以对二维数组进行types转换
#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; }