C++ 数组作为函数参数

本文最后更新于:2020年11月19日 上午

简述

把数组作为函数参数其实是个很常见也比较基础的问题,但初学的话肯定很头疼,长时间没用可能也会忘掉~

首先需要明确一点:下标运算符 [] 的优先级高于解引用运算符 *

关于运算符的优先级,可以参考这里: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 数组其实就没这些必要了,另外也可以使用智能指针,以使函数可以接受任意维度的数组,具体的实现就不讨论了~


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!