到目前为止,我有这个:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
并且我在代码中的其他地方声明了一个数组:
double anArray[10][10];
但是,调用
myFunction(anArray)
会给我一个错误。传递数组时,我不想复制该数组。对
myFunction
所做的任何更改都将改变状态anArray
。如果我理解正确,我只想将指向2D数组的指针作为参数传递。该函数还需要接受不同大小的数组。因此,例如,[10][10]
和[5][5]
。我该怎么办?#1 楼
将3D数组传递给函数的方法有以下三种:参数是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);
评论
@Overflowh您可以使用array [i] [j]获得array的元素:)
– shengy
13年5月27日在3:15
对于第一种情况,该参数可以声明为int(* a)[10]。
–扎卡里
2013年6月13日14:38在
对于第二种情况,可以将参数声明为int **。
–扎卡里
13年6月14日,下午3:14
@Zack:您是对的,实际上只有两种情况;一个是指向指针的指针,另一个是指向大小为n(即int(* a)[10])的整数数组的单个指针。
–legends2k
13年7月10日在10:44
情况2和3不是2D阵列,因此此答案具有误导性。看到这个。
–伦丁
15年6月16日在8:05
#2 楼
固定大小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;
}
}
先前方法的C等效项是通过指针传递数组。这不应该与传递数组的衰变指针类型(3)混淆,后者是一种常见的流行方法,尽管比此方法安全性较低,但更灵活。与(1)一样,当数组的所有维度都是固定的并且在编译时已知时,请使用此方法。请注意,在调用函数时,应通过
process_2d_array_pointer(&a)
传递数组的地址,而不是通过衰减process_2d_array_pointer(a)
传递第一个元素的地址。Variable Size
这些都是从C继承的,但是不太安全,编译器无法检查,以确保呼叫者通过了所需的尺寸。该功能仅基于调用者传递的维度作为维度。这些方法比上述方法更灵活,因为可以将不同长度的数组不变地传递给它们。
要记住,没有像将数组直接传递给C中的函数那样的事情[而在C ++中,它们可以通过作为参考(1)]; (2)将指针传递给数组,而不是数组本身。始终按原样传递数组将成为指针复制操作,这得益于数组衰减为指针的性质。
3。通过(值)指向衰变类型的指针
// 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**
}
评论
如何将动态分配的数组传递给C ++中的函数?在C11标准中,可以为诸如fn(int col,int row,int array [col] [row])之类的静态和动态分配的数组完成:stackoverflow.com/questions/16004668 / ...我已经对此问题提出了问题:stackoverflow.com/questions/27457076/…
– 42n4
2014-12-13 9:16
@ 42n4情况4涵盖了(同样适用于C ++)。对于动态分配的数组,仅循环内的行将从b [i] = a [i]变为;到b [i] = new int [10];。也可以使b动态分配int ** b = int * [5];而且仍然可以按原样工作。
–legends2k
2014-12-15 6:42
寻址array [i] [j]如何在4)的函数中起作用?因为它已收到ptr到ptr的信息,并且不知道最后一个维的值,所以执行正确的地址转换需要哪一个?
–user1234567
2014年12月16日下午16:27
array [i] [j]只是指针算术,即指向指针数组的值,它将添加i并将其结果取消引用为int *,然后将其添加j并取消对该位置的引用,从而读取一个int。因此,不,它不需要知道任何维度。但是,这就是重点!编译器秉承程序员的信念,如果程序员不正确,则会出现未定义的行为。这就是我提到案例4是最不安全的选择的原因。
–legends2k
2014-12-17 3:04
在这种情况下,结构可以很好地为您服务。
– Xofo
18年11月16日在18:17
#3 楼
对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;
}
打印语句显示通过引用(显示变量的地址)来传递数组
评论
您应该使用%p来打印指针,即使这样,也必须将其强制转换为void *,否则printf()会调用未定义的行为。此外,在调用函数时,请勿使用addressof(&)运算符,因为函数期望参数类型为double(*)[size_y],而当前您将它们传递给double(*)[10] [10]和double (*)[5] [5]。
–user529758
13-10-20在8:26
如果您使用的是模板,则将两个维度都用作模板参数会更合适,并且会更好,因为可以完全避免低级指针访问。
–legends2k
2014年3月24日12:43
仅当在编译时知道数组的大小时,此方法才有效。
–John Doe
17/12/27在16:51
上面答案中的@Georg代码正是我所建议的。它适用于GCC 6.3-在线演示。您是否忘记了将参数作为参考?
–legends2k
18年11月29日在5:39
#4 楼
惊讶的是没有人提到这一点,但是您可以简单地在任何支持[] []语义的2D模板上。数据结构,例如
std::vector<std::vector<T>>
或用户定义的类型,以最大化代码重用。评论
这应该是正确的答案。它解决了所有提到的问题,这里没有提到。类型安全,数组的编译时间不兼容,没有指针算术,没有类型转换,没有数据复制。适用于C和C ++。
– OpalApps
18/12/7在16:10
好吧,这适用于C ++。 C不支持模板。在C中执行此操作将需要宏。
–枪手
19年5月21日在21:22
#5 楼
您可以这样创建函数模板: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);
}
评论
size_t是数组索引比int更好的类型。
–安德鲁·托马佐斯(Andrew Tomazos)
13年7月10日在11:42
#6 楼
anArray[10][10]
不是指向指针的指针,它是一个连续的内存块,适合存储100个double类型的值,编译器知道如何寻址,因为您指定了尺寸。您需要将其作为数组传递给函数。您可以省略初始维的大小,如下所示:void f(double p[][10]) {
}
但是,这将不允许您传递最后一个维不是10的数组。
C ++中最好的解决方案是使用
std::vector<std::vector<double> >
:它几乎一样高效,并且方便得多。评论
我更喜欢这种解决方案,因为std库非常有效-通过我喜欢dasblinkenlight的方式;我曾经用过dasblikenlicht
– mozillanerd
2012年1月18日在21:34
效率差不多吗?是的,对。指针追逐总是比非指针追逐更昂贵。
–托马斯·埃丁
2015年9月24日19:07在
#7 楼
一维数组会衰减为指向数组中第一个元素的指针指针。而2D数组衰减到指向第一行的指针。因此,函数原型应该是-void myFunction(double (*myArray) [10]);
我更喜欢
std::vector
而不是原始数组。#8 楼
您可以执行以下操作...#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
评论
在这种情况下,为什么我会弄乱数组的唯一原因是,因为人们缺乏关于数组指针如何工作的知识。
–伦丁
15年6月16日在8:09
i变量必须乘以列,而不是行,除非在这种情况下列和行相等
–安德烈·切尔努卡(Andrey Chernukha)
16年5月5日在21:51
*(a +(i * cols)+ j)为真。不是这个*(a +(i *行)+ j)。请修复它
– Sadegh
4小时前
#9 楼
这是向量矩阵的向量示例#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
#10 楼
我们可以使用几种方法将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
– David C. Rankin
12月6日6:20
#11 楼
传递多维数组的重要事项是:无需指定
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;
}`
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;
}`
#12 楼
您可以使用C ++中的模板工具来执行此操作。我做了这样的事情:template<typename T, size_t col>
T process(T a[][col], size_t row) {
...
}
这种方法的问题是,对于您提供的col的每个值,都会使用模板实例化一个新的函数定义。
so,
int some_mat[3][3], another_mat[4,5];
process(some_mat, 3);
process(another_mat, 4);
将模板实例化两次,以产生2个函数定义(其中col = 3的一个和col = 5的一个)。
#13 楼
如果要将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);
#14 楼
如果要将动态大小的二维数组传递给函数,则可以使用一些指针。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);
}
#15 楼
您可以省略最左边的尺寸,因此有两个选择: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 [][]
是包含字符的单个存储块。这会影响编译器如何翻译代码以及最终性能如何。源代码
评论
无法将参数3从'double [10] [10]'转换为'double **'可接受的答案仅显示2种技术(其(2)和(3)相同),但是有2种将2D数组传递给函数的独特方法。
严格来说,是的,它们不是2D数组,但是这种惯例(尽管导致UB)是由一个指针数组组成的,每个指针指向一个1D数组,这似乎很普遍:(具有mxn的扁平1D数组长度,使用辅助函数/类来模拟2D数组可能更好。
EASIEST- func(int * mat,int r,int c){for(int i = 0; i