固定大小
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. 通过传递指向衰变类型的指针进行传值
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个整数的数组的单个指针,而这种语法
看起来像是二维数组,但实际上是指向包含10个整数的数组的相同指针。在这种情况下,我们知道单行中元素的数量(即列大小,在此处为10),但行数未知,因此必须作为参数传递。在这种情况下,存在一些安全性,因为编译器可以标记传递了第二维大小不等于10的数组指针。第一维是可变部分,可以省略。
请参见此处的原理,了解为什么只允许省略第一维。
4. 通过指向指针的指针进行传递
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>
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a);
process_2d_array(a, 5);
int *b[5];
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
process_pointer_2_pointer(b, 5, 10);
}
func(int* mat, int r, int c){ for(int i=0; i<r; i++) for(int j=0; j<c; j++) printf("%d ", *(mat+i*c+j)); }
。像这样调用-int mat[3][5]; func(mat[0], 3, 5);
。 - Minhas Kamal