单从字面上来看似乎很难分清它们是什么,我们先来看看指针数组和数组指针的定义。
指针数组:数组类型 *数组名[数组长度];
例:int *parr[5];
数组指针:类型名 (*指针名)[数组长度];
例:int (*parr)[5];
现在我们再来通过上面的定义来分析一下指针数组和数组指针。
int *parr[5];
因为优先级的关系,parr先与[]结合,说明parr是一个数组,然后再与*结合说明数组parr的元素是指向整型数据的指针。元素分别是parr[0],parr[1]...parr[4],相当于定义了5个整形指针变量。所以parr就是数组元素是指针的数组,本质为数组。
int (*parr)[5];
parr先与*结合,形成一个指针,该指针指向的是有5个整形元素的素组,parr就是指向数组元素地址的指针,本质为指针。
这下对指针数组和数组指针应该有了初步的了解了吧,接下来我们来通过一段代码进一步了解一下。
//
// Created by wangc on 2020/8/23.
//
#include <stdio.h>
int main() {
int arr[4][4] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
int (*p1)[4]; //数组指针
int *p2[4]; //指针数组
p1 = arr;
printf("使用数组指针的方式访问二维数组arr\n");
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
printf("arr[%d][%d]=%d\t",i,j,*(*(p1+i)+j));
}
printf("\n");
}
printf("\n使用指针数组的方式访问二维数组arr\n");
for (int k = 0; k < 4; ++k) {
p2[k] = arr[k];
}
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
printf("arr[%d][%d]=%d\t",i,j,*(p2[i]+j));
}
printf("\n");
}
return 0;
}
运行结果:
从结果可以看出我们成功的使用数组指针和指针数组的方式访问了二位数组。
在分析数组指针和指针数组是如何访问二维数组之前,我们通过下面一段代码来学习一下表示二维数组每行起始地址的方式。
//
// Created by wangc on 2020/8/23.
//
#include "stdio.h"
int main(){
int arr[4][4] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
for (int i = 0; i < 4; ++i) {
printf("使用arr+i求得二维数组arr第%d行的起始地址为%d\n",i+1,arr+i);
printf("使用arr[i]求得二维数组arr第%d行的起始地址为%d\n",i+1,arr[i]);
printf("使用*(arr+i)求得二维数组arr第%d行的起始地址为%d\n",i+1,*(arr+i));
printf("使用&arr[i]求得二维数组arr第%d行的起始地址为%d\n",i+1,&arr[i]);
}
return 0;
}
运行结果:
在上面的代码中,我们使用了4种方式来获得每行的起始地址,因此行起始地址的表示方式并不唯一。
下面接着分析数组指针和指针数组是如何访问二维数组的,先看数组指针的访问方式。因为数组指针指向的是一个有4个整型元素的数组,所以可以把二维数组arr看成由4个元素arr[0],arr[1],arr[2],arr[3]组成,每个元素都是含有4个整型元素的一维数组,所以当在代码中使用p1=arr的时候,p1就指向了二维数组的第一行的首地址。在接下来的访问中,由于p1指向的类型是int [4],所以从p1到p1+1的变化值为44个字节,即p1+1=15726992。从前面的运行结果中可以发现,p1+1刚好指向第二行的起始地址。通过p1+i刚好能够取遍每行的起始地址,有了每行的起始地址之后,就可以通过“*(*(p1+i)+j)”来取出二维数组中每行的每一个元素。指针数组的访问方式要更容易一些,因为定义的指针数组p2由4个元素p2[0],p2[1], p2[2], p2[3]组成,每个元素都是一个整型指针,所以只需要在程序中取出每行的起始地址并放到p2指针数组对应的元素中,就可以访问二维数组arr中的元素了。
所以,在程序中使用指针数组和数组指针的时候,必须对它们有清晰的认识,要知道它们的本质是什么,以及如何使用。