欢迎来到博主的专栏——C语言进阶指南
博主的id:reverie_ly
数组指针
数组指针的标准声明如下
type (*parr)[num]
type是指针指向的数组的类型,[num]是指针指向的数组的元素大小。
顾名思义,数组指针就是用来指向数组的指针变量
int arr[5];
int*p=arr;
int (*parr)[5]=arr;
与一级指针不同(int*),这个指针变量的类型是(int*[5]).
虽然parr与p的赋值都是arr,但是这个arr在不同的变量上的类型是不一样的。
p中的arr是发生了类型降级的,也就是说arr从int[5]的类型变成了int*
而parr中的arr是int*[5]的,发生了类型转换,使arr的类型从int[5],变成了int*[5].
从下面的示例中也能看出区别
#include<stdio.h>
int main()
{int arr[5] = { 1,2,3,4,5 };int* p = arr;int(*parr)[5] = arr;for (int i = 0; i < 5; i++){printf("%d ", (*parr)[i]);//parr先被解引用,再取下标访问}for (int i = 0; i < 5; i++){printf("%d ", p[i]);//p直接去下标访问}return 0;
}
在该例中,parr先被解引用成int[5]类型的变量(即数组arr的类型),再去下标进行访问。
也就是说(*parr)是等价于(arr)的。
而p是利用了指针算术运算的原理进行访问的,在前面的文章中提到过
*(p+i)=p[i]
注意区分这两种实现方法的数据类型在解引用时的区别,parr在使用前被解引用成int[5]类型的arr的地址。再解引用成int类型的arr的元素。
而p则是从(int*)类型解引用成int类型的arr的元素。
数组指针与指针数组
数组指针与指针数组的声明非常类似。这里将两种声明进行对比
数组指针:
type (*parr)[num]
指针数组:
type *parr[num]
可以发现指针数组与数组指针的区别仅仅在于标识符与声明结合优先度不同
在没有括号的情况下,标识符优先与[]结合成为一个数组的声明符
用括号()将标识符与*优先结合,使得标识符与*组成一个指针的声明符
虽然指针数组与数组指针的声明非常相似,但是性质是完全不同的。
数组指针是一个指针,它是一个指向指定类型数组的指针,比如int(*parr)[5],这个数组指针指向的是int[5]类型的数组。(一级指针也能指向数组,但是一级指针指向的只是数组的首元素(int*)的地址,而数组指针指向的是整个数组[int[num]],这一特点数组指针将在二维数组的使用中展现出来)
而指针数组是一个数组,数组中的元素数据类型都是指针类型
数组指针与二维数组
我们可以发现在上面的例子中数组指针是能被指针替代的,那么数组指针的意义何在?
回顾前面的文章可以了解到,指针进行加减N的算术运算时,会跳过(或往前)N个等数据类型大小的字节。比如int*类型的指针加1时会跳过int类型大小(4字节)的字节。那么数组指针进行算术加减时跳过几个字节呢?
int arr[5];
int(*parr)[5]=arr;
printf("%p\n%p",parr.parr+1);
运行发现parr+1比parr高20个字节的地址。这是由于int*[5]对应的数据类型是int5。而int[5]类型对应的字节就是20字节。
对应到二维数组中,如果程序构造一个数组指针指向二维数组
int arr[4][5] = {0};int(*parr)[5] = arr;
那么二维数组arr每一行都有五个int类型的元素(20字节),而parr+1跳过20字节就是跳过一行。
parr+1对应的地址是&arr[1],那么解引用parr+1则等价于arr[1].
打开vs中的监视窗口可以发现,arr[1]是int[5]类型的数据(arr是int[4][5]).于是将(int*[5])类型的parr+1解引用,parr的类型也变成int[5],而且parr+1的地址和arr[1]的地址是一样的!!!
那么也就是说 *(parr+1)等价于arr[1],那么*(parr+1)[2]不就等价于arr[1][2]么。 *正是如此,我们就可用通过数组指针来操作二维数组了
int main()
{int arr[4][5] = {0};int(*parr)[5] = arr;for (int i = 0; i < 4; i++){for (int j = 0; j < 5; j++){(*(parr + i))[j] = j;//要注意[]的优先级比*高,所以要先将parr+i解引用。}}return 0;
}
不过上例中,数组指针的作用还没有完全体现出来,既然二维数组就能实现的程序,为什么还要数组指针来作为中间商?
事实上数组指针的真正作用在于,数组指针作为函数原型的参数时。此时函数的参数可以传递二维数组的数组名作为函数的实际参数。
void print(int(*parr)[5], int row-1, int col-1)
{printf("%d",(*(parr + row))[col]);
}
这个函数的意义则是打印出某行某列的数。如果让上上个例子调用这个函数,那么函数的调用语句可以为
print(arr,2,4);//打印第2行第四列的元素(arr[1][3]).