6.4.6 数组指针
所谓数组指针,就是能够指向一个数组的指针。前面所看到的指针所指向的对象,基本上都是单个的变量或字符串常量,而数组指针所指向的对象是一个数组。
例如,我们定义了一个长度为 5 的 int 类型数组 a:
int a[5] = {10, 20, 30, 40, 50};
它的 5 个数组元素分别被初始化为 10、20、30、40、50。
下面再定义一个能够指向该数组的指针 pArr:
int (*pArr)[5];
这表示定义了一个能够指向长度为 5 的 int 类型数组的数组指针 pArr。需要注意的是,这里的小括号不可省略,否则,就变成定义一个长度为 5 的 int *类型的指针数组。
接下来让数组指针指向数组 a:
pArr = &a;
通过取地址符取得数组 a 的地址,赋值给数组指针 pArr,这相当于让数组指针 pArr 指向了数组 a。注意,不要漏掉取地址符,否则将变成将值为 10 的那个数组元素的内存地址赋给数组指针 pArr,虽然地址值是相同的,但这是不合理的,在编译时,也会给出警告信息。因为我们想要获取的是整个数组的内存地址,而不是数组首元素的内存地址,所以应该在数组名 a 的前面加上取地址符。
当然,我们也可以在定义数组指针时,以初始化的方式让数组指针 pArr 指向数组 a,例如:
int (*pArr)[5] = &a;
现在可以通过数组指针 pArr 访问数组 a 中的各元素,例如:
*(*pArr + 1)
在小括号中,“*pArr”对数组指针 pArr 进行解引用,由于 pArr 是指向数组 a 的指针,因此,经过解引用就可以访问到它所指向的对象,即数组 a。对于数组 a 来说,它又表示数组首元素的内存地址,即一个指向数组首元素的指针,因此对它进行算术运算,加上 1 之后,就会得到一个指向数组第二个元素的指针。最后,再通过小括号外的解引用运算符,即可访问到数组中第二个数组元素。
下面用一个 for 循环,通过数组指针来访问并打印数组 a 中的所有元素:
for(int i = 0; i < 5; ++i) printf("%d ", *(*pArr + i));
编译运行程序,结果如下:
10 20 30 40 50
当然,也可以在访问到数组 a 后,再使用下标法来访问数组中的元素,例如:
for(int i = 0; i < 5; ++i) printf("%d ", (*pArr)[i]);
在第 5 章中介绍过,可以将二维数组看成是由一维数组构成的数组,也就是如果把二维数组看成是一维数组的话,那么这个数组中的所有元素又都是一维数组类型。例如:
如果将二维数组 b 看成是一维数组的话,那么它就有 2 个元素,而每个元素都是长度为 3 的 int 类型数组,即元素值为 10、20、30 的一维数组是它的第 1 个元素,元素值为 40、50、60 的一维数组是它的第 2 个元素。
由于数组名即为数组首元素的内存地址,所以 b 就是第一个长度为 3 的 int 类型数组的内存地址。也就是可以把 b 看成是一个指向长度为 3 的 int 类型数组的指针,即 b 是一个数组指针。
于是,可以定义一个数组指针 pArr,它指向一个长度为 3 的 int 类型数组:
int (*pArr)[3] = b;
将数组名 b 作为初始值赋给数组指针 pArr。由于 b 本身就是一个指向长度为 3 的 int 类型数组的指针,因此,不需要在它的前面再加取地址符了。
下面来看一下如何通过数组指针访问二维数组中的元素。例如,我们想要访问第 2 行第 3 列的数组元素,可以这样:
*(*(pArr + 1) + 2)
内层小括号内表达式为“pArr + 1”,我们知道 pArr 是指向第一个长度为 3 的 int 类型数组的指针,经过加 1 之后,它会产生一个新的数组指针,这个新指针所对应的内存地址为 pArr 的内存地址加上一个长度为 3 的 int 类型数组的大小,结果为第二个长度为 3 的 int 类型数组的内存地址,即通过表达式“pArr + 1”得到了一个指向第二个长度为 3 的 int 类型数组的指针。然后通过内层小括号前的解引用运算,即可访问第二个长度为 3 的 int 类型数组,就好像得到了名字为“b[1]”的一维数组。由于可以将一维数组名看成指向首元素的指针,因此,在外层小括号内将其与 2 进行相加,即得到了指向该一维数组中第 3 个数组元素的指针。最后,通过外层小括号前的解引用运算,即可访问该一维数组中的第 3 个数组元素,就好像得到了名字为“b[1][2]”的 int 类型数组元素。是不是和使用下标来访问数组元素非常像?
下面用一个双层循环来访问并打印二维数组中的所有元素:
编译运行程序,结果如下:
10 20 30 40 50 60
把“*(pArr + i)”换成下标表示法就变为“pArr[i]”,把“*(*(pArr + i) +j)”换成下标表示法就变为“pArr[i][j]”。也就是可以将数组指针当成数组名一样来使用,即可以将双层循环修改为:
再次编译运行,结果和之前一样。
可见,对一维数组来说,可以将其首元素的内存地址看成一个普通类型的指针,既可以使用指针法来访问数组元素,也可以像数组名一样使用下标法来访问数组元素。而对于二维数组来说,可以将其中的第一行(由该行各列构成的一维数组)看成首元素,而将首元素的内存地址看成一个数组指针,同样既可以使用指针法来访问数组元素,也可以像数组名一样使用下标法来访问数组元素。
最后,当我们看到一维数组名时,就可以将其看成一个普通类型的指针,而看到二维数组名时,就将其看成一个数组指针,即该指针能够指向一个数组。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论