指针数组与数组指针

今天刚好复习了C++ Primer中关于数组指针的内容,理清了数组指针的几个问题。

指针数组与数组指针定义

先看指针数组与数组指针的定义吧:指针数组,主体是数组,数组内存放的元素是指针;而对于数组指针,核心是指针,指针指向的是一个数组。

1
2
3
4
// 指针数组
int *ptrs[10];
// 数组指针,指向的是一个含有10个元素的指针
int (*parray)[10] = &arr;

解析数组指针

指针数组其实很好理解,难点在于数组指针容易让人糊涂,先看下面这样一段程序吧。

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

int main()
{
int arr[2][4] = { 1,2,3,4,5,6,7,8 };
int(*Parray)[2][4] = &arr;

cout << "arr: " << arr << endl;
cout << "*arr: " << *arr << endl;
cout << "**arr: " << **arr << endl;

cout << "Parray: " << Parray << endl;
cout << "*Parray: " << *Parray << endl;
cout << "**Parray: " << **Parray << endl;
cout << "***Parray: " << ***Parray << endl;
}

程序的运行结果如下所示:

1
2
3
4
5
6
7
arr:       009CFDE8
*arr: 009CFDE8
**arr: 1
Parray: 009CFDE8
*Parray: 009CFDE8
**Parray: 009CFDE8
***Parray: 1

很多人看到这可能直接就迷糊了,这些东西到底都是指向的啥?

要理解这里的各种指针的区别,首先需要理解指针指向的基本元素是什么,即*p指向什么,*(p+1)又指向什么,理解了这一点,就能比较容易理解上面的各种指针的含义。

对于数组而言,数组名代表的是指向数组内基本元素的地址,这里的基本元素指什么呢?对于一元数组(int a[3]={1, 2, 3})而言,基本元素代表数组内的每一个数字,即*a代表1,*(a+1)代表2。而对于二元数组int arr[2][4] = { 1,2,3,4,5,6,7,8 };,这里的基本元素就是二元数组的一行,我们可以这样理解,这个数组相当于有2个位置(把最靠近数组名的数字看做数组的基本元素的数量),然后每个位置存放了一个容量为4的数组。因此*arr指向的是数组的第一个基本元素(即第一行),*(arr+1)指向数组的第二个基本元素(即第二行)。

理解上面这一段话之后,我们就能理解上述程序中的arr*arr**arr各自代表什么了。arr代表二元数组的第一个基本元素(即第一行)的地址,*arr对第一行的地址解引用了,相当于指向了这第一行的第一个元素的地址。注意这里的区别,arr+1是会使地址往后挪一行所占用的字节,而(*arr)+1是只会往后挪一个数字所占用的字节,因为*arr指向了第一行的第一个元素,自然**arr就输出了该元素的值1。

我们再来看Parray,因为我们把(*Parray)用括号括起来了,因此Parray首先是一个指针,然后看它后面[2][4],说明他是一个指向大小为[2][4]的数组的指针。所以Parray代表这个数组整体的首地址,即Parray指向这个数组,Parray+1指向这个数组整体的尾元素的下一个地址。随后层层解引用,*Parray代表数组的第一行的首地址,*Parray代表数组的第一行的第一个元素的地址,***Parray代表数组的第一个元素的数值。

再来看看刚才那一段代码:

1
2
3
cout << Parray + 1 << endl;
cout << *Parray + 2 << endl;
cout << **Parray + 8 << endl;

输出:

1
2
3
007EFCF8
007EFCF8
007EFCF8

可以看到输出是一致的,因为Parray指向整个数组,因此+1会往后挪整个数组所代表的字节数;而对于*Parray,由于Parray指向的是一行,因此需要+2,对于**Parray同理。

总结

在理解数组指针的时候,很多人看见那几个*号直接就晕了,虽然它们指向的元素的首地址是相同的,但是它们+1之后所指向的位置就完全不同了,因此理解这里的关键在于理解它们+1之后都指向了什么位置。