指向数组的指针与指向数组首元素的指针究竟有什么区别呢?有人说,这二者不是一回事么?它们就是同一个东西啊!然而,事实并非如此,下面我细细道来。

首先,我们定义一个一维数组来具体分析一下,这个数组含有10个整数:

1
int ia[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

我们再来定义指向这个数组的指针:

1
int (*p)[10] = &ia;

我们知道,在使用数组名的时候,一般情况下实际使用的都是数组首元素的地址(decltype等少数特殊情况除外,这类操作有其特殊的含义),我们可以这样来理解,ia 相当于一个变量,在这个变量中存放的是数组首元素的地址,即相当于这个变量是指向数组首元素的指针。

我们再回过头来看看指向数组的指针,它是对 ia 取地址,也就是说,ia 相当于一个变量,对 ia 取地址得到的是 ia 的地址,即是数组首元素的指针的地址。

从下图看以上分析过程可以一目了然,从指向数组的指针经过两次解引用后可以得到数组首元素。

从以上分析过程我们知道:

指向数组的指针本质上是指向数组首元素的地址的指针,由它经过一次解引用后得到指向数组首元素的地址,由它经过两次解引用后得到相应数组的首元素

那么,事实是不是我们以上分析的那样呢?下面我们通过二维数组来分析一下,我们知道,所谓 二维数组 实际上指的是 数组的数组,如 ia[2][3] 表示的是两个元素个数为 3 的一维数组的数组,二维数组的数组名实际上是这个二维数组首元素的地址,即是这个二维数组的第一个一维数组的地址,由以上分析过程我们知道,某个一维数组的地址实际上是数组首元素指针的地址,由它经过两次解引用后可以得到数组的首元素,基于这个原理,用一个简单的程序来验证以上过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using std::cout;
using std::endl;

int main()
{
int ival[3][4] = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11}
};
for (int (*i)[4] = ival; i != ival + 3; ++i) { // i 是指向这个二维数组中第一个一维数组的指针
for (int *j = *i; j != *i + 4; ++j) // 对 i 解引用得到的是某个二维数组首元素的地址
cout << *j << ' '; // 对 j 解引用得到相应的元素
cout << endl;
}

return 0;
}

以上程序最后的运行结果为:

0 1 2 3
4 5 6 7
8 9 10 11

完全符合期望中的结果,由指向某个一维数组的指针得到最后的具体每一个元素经历了两次解引用过程,由此证明我们以上的分析是正确的。