起因 链接到标题

曾经看到老谭的书上有这样一行

注意:不要写成 int (*p)[4]; ,这是指向一维数组的指针变量

我单纯的认为 p 代表一个数组了,但后来有一位同学向我提出的疑问令我发现我终究还是年轻了,所以就打算写这样一篇博客来好好辨析一下数组、指针、数组指针和指针数组这四个玩意。

数组 链接到标题

这个就很基础了,定义语句就是类型关键词 + 数组名 + 若干个中括号(用于声明数组的维数与每一维的大小),例如:

int a[2][3]

我们都知道事实上这是六个线性连续的内存空间,但其实还是有一些神奇的底层原理的,这个我们就留到数组指针部分去讲了。

指针 链接到标题

简单来说指针就是一个储存了某一内存地址的类型,它的长度取决于系统的位数,在64位系统下是64位,也就是8字节长,在32位系统下就是32位,4字节长了。

我们可以利用指针和解引用符号 *(也叫取值符)来快速获取它所指向的内存空间里储存的数据,也可以用取地址符&来快速获取某一数据的内存地址。

指针可以用于实现动态规划内存、链表、函数传址等操作,也能使两个占据内存非常大的变量在交换时损失极小的空间(大小为指针的长度)(当然 c++ 中的swap可以在不损失空间的情况下完成交换)。

定义为类型关键词 + * + 指针名,例如

int *a

ps:因为定义几维数组就需要在指针名前加几个 *,所以我们就称这样的程序员为几星程序员(雾)

指针数组 链接到标题

在了解了数组和指针后,指针数组也就变的容易理解了,首先它是这样定义的:类型关键词 + * + 数组名 + 若干个中括号(用于声明数组的维数与每一维的大小),例如:

int *a[3]

众所周知,[] 的优先级是高于 * 的,所以 a 会先与 [] 结合,使 a 成为一个数组,然后 * 告诉编译器,这是指针类型的,最终的结果就是这成为了一个一维数组,有三个元素,每个元素都是 int * 类型的,简单来说就是 a 数组里每一个元素都是整型指针。

当然这个指针数组可以用 new 函数或者其他的来分配更大的内存,使其成为一个二维数组。

数组指针 链接到标题

这是这四个里我认为最难理解的,首先显示定义吧,类型关键词 + (* + 指针名 + ) + 若干个中括号(用于声明数组的维数与每一维的大小),例如:

int (*a)[3]

然后我们来解决一下前面卖的关子,假设我们有一个二维数组 b,它的声明如下:

int b[2][3]

虽然 [] 是从左往右执行的,但我们可以这样来理解这整个过程,找三个 int 类型所需的连续内存空间(每个是4个字节),把它们打包到一起,成为一个一维三元素数组,然后找两个一维三元素数组所需的连续内存空间(事实上是先找一整块可以放得下整个数组的内存空间然后一点点分的,我这样描述是方便理解),成为一个二维二*三元素数组,这样就定义完成了。

而我们定义的数组指针,恰好是一个指向一维三元素数组的指针,所以我们可以将 b 数组内的某个一维三元素数组的地址赋给 a,例如第一个一维三元素数组:

a = b

这时候我们就发现 a,*a,b ,*b 是同一个值了,因为他们都是 b 数组的首元素地址。

这样理解我们可以发现数组其实是将一个子层的元素打包起来交给本层的,然后再将本层的所有元素打包起来交给父层的,到时候再看到 int (*)[3]这样的报错也就不会一头雾水了。