固定尺寸
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)
。
可变大小
它们是从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**
}