我有一个函数,我想把它作为一个参数,一个大小可变的2D数组。
到目前为止,我得到的是:
void myFunction(double** myArray){
myArray[x][y] = 5;
etc...
}
并且我在代码的其他地方声明了数组:
double anArray[10][10];
但是,调用MyFunction(anArray)
会给我一个错误。
我不想在传入数组时复制它。 在MyFunction
中所做的任何更改都应更改AnArray
的状态。 如果我理解正确,我只想作为参数传入一个指向2D数组的指针。 函数还需要接受不同大小的数组。 例如,[10][10]
和[5][5]
。 我怎么能这么做呢?
将2D数组传递给函数有三种方法:
>
参数是一个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++中,通过引用传递数组而不丢失维度信息可能是最安全的,因为不必担心调用方传递不正确的维度(当不匹配时编译器标记)。 但是,对于动态(自由存储)数组,这是不可能的; 它只适用于自动数组(通常是堆栈生存数组),也就是说,维数应该在编译时就知道。
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)
传递,而不是通过decayprocess_2D_array_pointer(a)
传递第一个元素的地址。
这些都是从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**
}
对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;
}
print语句用于显示数组是通过引用传递的(通过显示变量的地址)