C++中数组真的存在吗 🤔?

深入理解C++二维数组

提问:定义一个3 * 3的数组arr,值分别为1~9,问arr[0][5]的值为多少?

首先定义一个二维数组,然后获取其地址:

int arr[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        std::cout << "arr[" << i << "][" << j << "] = " << arr[i][j] << ", address = " << &arr[i][j] << std::endl;
    }
}
20230407150212
20230407150212

通过打印地址发现每个元素地址之间差4,也就是一个int的长度(一般情况下int型变量为4个字节),而且地址也是连续的,说明申请了一个二维数组,然后申请了一段连续的地址空间来存放这些数据。

然后我们打印一下arr、arr[0]、arr[0][0]的地址:

std::cout << "Address of arr: " << &arr << std::endl;
std::cout << "Address of arr[0]: " << &arr[0] << std::endl;
std::cout << "Address of arr[0][0]: " << &arr[0][0] << std::endl;
20230407150901
20230407150901

可以看出,三者地址是一样的,这说明三者都指向数组起始地址,变量名arr也就是数组的起始地址,那么arr是什么数据类型呢?

打印一下arr、arr + 1、 arr + 2、arr[0]、arr[1]、arr[2]的地址:

    // 打印arr的地址
    std::cout << "Address of arr: " << arr << std::endl;

    // 打印arr + 1的地址
    std::cout << "Address of arr + 1: " << arr + 1 << std::endl;

    // 打印arr + 2的地址
    std::cout << "Address of arr + 2: " << arr + 2 << std::endl;

    // 打印arr[0]的地址
    std::cout << "Address of arr[0]: " << arr[0] << std::endl;

    // 打印arr[1]的地址
    std::cout << "Address of arr[1]: " << arr[1] << std::endl;

    // 打印arr[2]的地址
    std::cout << "Address of arr[2]: " << arr[2] << std::endl;
20230407151423
20230407151423

显而易见,arr + n 等价于 arr[n],arr[n]是指针类型,那么arr就是比arr[n]多一维的维指针

再来看一下arr[0][0]、arr[1][0]、arr[2][0]的地址和值(前面已经打印出来):

// 打印arr[0][0]、arr[1][0]、arr[2][0]的地址和值
std::cout << "Address of arr[0][0]: " << &arr[0][0] << ", value = " << arr[0][0] << std::endl;
std::cout << "Address of arr[1][0]: " << &arr[1][0] << ", value = " << arr[1][0] << std::endl;
std::cout << "Address of arr[2][0]: " << &arr[2][0] << ", value = " << arr[2][0] << std::endl;
20230407152029
20230407152029

可以看到和上述arr[n]地址一样,表示的每一行第一列的值,显而易见arr[n]指向的就是每行首元素的地址,也就是一维指针,那么arr就是二维指针,来验证一下:

int *p = a; // 不通过

实验发现报错,继续看

int* p = *arr;
std::cout << "Address of p: " << p << ", Address of arr: " << arr << std::endl;
20230407152833
20230407152833
std::cout << "the value of p is: " << p << std::endl;
std::cout << "the value of *p is: " << *p << std::endl;
std::cout << "the value of p+1 is: " << p+1 << std::endl;
std::cout << "the value of *(p+1) is: " << *(p+1) << std::endl;

std::cout << "a[1][1] inferred from p : " << *(p+1*3+1) << std::endl;
20230407153004
20230407153004

对a解引用后确实是一个地址,所以可以定义指针,并且可以用加偏移量的方式得到a[1][1]

对于二维数组arr[i][j],a[m][n]表示其中的一个值:

  • arr[m][n] == *(*(arr + m) + n) == *(*arr + m * i + j)

总的来说数组的本质还是由指针封装起来的数据类型,所以从根本上来说可以从地址上解决,遇到一些奇奇怪怪的问题也可以由地址来解决,比如本文的问题:

20230407154547
20230407154547

另外,数组可以越界,这是C++所决定的,想要不越界只能人为的去限制