C++ 数组作为函数参数
本文最后更新于:2023年2月8日 晚上
简述
把数组作为函数参数其实是个很常见也比较基础的问题,但初学的话肯定很头疼,长时间没用可能也会忘掉~
首先需要明确一点:下标运算符 []
的优先级高于解引用运算符 *
。
关于运算符的优先级,可以参考这里:https://zh.cppreference.com/w/cpp/language/operator_precedence
分析两种传递方式
方式一
以下是分别将一维、二维、三维数组将作为函数参数传递第一种方式,即声明相同大小的数组类型:
#include <iostream>
using namespace std;
// 一维数组作为参数
void array1d(int arr1[2]) {
cout << typeid(arr1).name() << endl; // 输出:int * __ptr64
}
// 二维数组作为参数
void array2d(int arr2[2][3]) {
cout << typeid(arr2).name() << endl; // 输出:int (* __ptr64)[3]
}
// 三维数组作为参数
void array3d(int arr3[2][3][2]) {
cout << typeid(arr3).name() << endl; // 输出:int (* __ptr64)[3][2]
}
int main() {
int arr1[2] = { 1, 2 };
array1d(arr1);
int arr2[2][3] = { {1,2,3},{3,4,5} };
array2d(arr2);
int arr3[2][3][2] = { {{1, 2}, {2, 3}, {3, 4}},{{5, 6}, {6, 7}, {7, 8}} };
array3d(arr3);
return 0;
}
了解数组的初始化的话我们还可以省略多维数组第一维的大小,将二维、三维数组的形式参数写成如下形式,编译器会根据实际传入的数组大小确定第一维的大小:
void array2d(int arr2[][3], int r, int n);
void array3d(int arr3[][3][2], int r, int c, int a);
调试观察一下,可以知道上述数组定义并初始化后,一维数组 arr1
的类型为 int[2]
,二维数组 arr2
的类型为 int[2][3]
,三维数组 arr3
的类型为 int[2][3][2]
。
但传递到函数中后,形式参数的类型却分别变为了 int*
、 int[3] *
和 int[3][2] *
。
实际上也并不难理解,我们知道数组名其实就是指向数组第一个元素的指针,因此上面指针类型的形参其实就分别是:
int*
指向 int 类型的指针,这里实际指向的是 int 型的一维数组;int[3] *
指向长度为3的 int 型数组的指针。虽然传入的是二维数组,但不变的是传入的是数组第一个元素的地址,对于一个二维数组即第一维的第一个数组的地址;int[3][2] *
指向数组中第一个元素,即3行2列的二维数组的指针。- 更多维的情况也同理~
方式二
根据上述方式一调试分析的结论,我们既然知道了形参的实际类型,因此可以直接把形参声明为该类型:
void array1d(int *arr1); // 一维
void array2d(int (*arr2)[3]); // 二维
void array3d(int (*arr3)[3][2]); // 三维
这种方式实际与方式一并没有什么区别,笔经数据类型都是完全一致的。
这里需要注意,不能把 int (*arr2)[3]
写成 int *arr2[3]
,原因就在于简述中谈到的运算符优先级的问题,这两种写法之间是有本质区别的,即指针数组和指向数组的指针之间的区别:
int (*arr2)[3]
是指向数组的指针,本质上是指针,只不过指向的是一个长度为3的 int 型数组;int *arr2[3]
是指针数组,本质是数组,存储的是3个 int 型的指针。
关于指针数组和指向数组的指针的更多内容可以自行查阅相关资料,这里就不多说啦🥱
更多一些思考
该部分更新于:2020 年 10 月 18 日
根据上面所讨论的,其实有一点是可以明确的,只要将数组作为参数传递,接收到的就一定是一个指针。
显然对于一个指针,它自身是没有维度的,只有它所指向的对象才可能有维度,这也就可以解释为什么一个二维数组作为参数,接收到的是一个指向一维数组的指针,因为这个指针自身唯有维度,它指向原二维数组的第一个元素,而原二维数组的第一个元素即一个一维数组。
也正因为指针没有维度,我们是不是可以这么理解:数组作为参数传递之后,就丢失了原数组第一个维度的信息。
至少我觉得这样理解是比较合理的,也因此,实际编程中,在传递一个数组的同时传递其维数,程序的可读性将更强。
例如,可以将上述方式二中的函数声明修改为如下所示:
void array1d(int *arr1, int d); // 一维
void array2d(int (*arr2)[3], int r, int c); // 二维
void array3d(int (*arr3)[3][2], int a, int b, int c); // 三维
最后呢,其实本文所讨论的均为一般的数组,如果说使用标准库类型 vector 数组其实就没这些必要了,另外也可以使用智能指针,以使函数可以接受任意维度的数组,具体的实现就不讨论了~