1.指针数组
指针数组是一个可以储存多个地址的数组
我们以int*这种数据类型来举例
int main()
{
int arr1[2]={1,2};
int arr2[2]={3,4};
int arr3[2]={5,6};
int* parr[3]={arr1,arr2,arr3};
return 0;
}
提前需要知道,对于一个一维数组,数组名就代表首元素的地址
由此可知,一个指针数组里可以存放多个指针变量
那么指针数组有什么实用意义呢?
使用指针数组,可以存储多个一维数组,再通过对指针的加减操作来访问每一个数组之中的元素
如下代码
int main()
{
int arr1[2] = { 1,2 };
int arr2[2] = { 3,4 };
int arr3[2] = { 5,6 };
int* parr[3] = { arr1,arr2,arr3 };
printf("%d\n", *(parr[0] + 1));
printf("%d\n", *(parr[1] + 0));
printf("%d\n", *(parr[2] + 1));
return 0;
}
执行结果从上到下为
2 ———— 0号数组的1号元素 解引用parr[0] + 1
3 ———— 1号数组的0号元素 解引用parr[1] + 0
6 ———— 2号数组的1号元素 解引用parr[2] + 1
通过这个例子可以发现,多个一维数组与一个指针数组的组合使用
可以用于模拟二维数组
2.数组指针
1)对于数组地址的讨论
首先,我们需要再重新认识一下数组地址的几种写法
int main()
{
int arr[5];
printf("%p\n", &arr);
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}
执行结果如下
arr和&arr[0]这两种表示方法毫无疑问,都是表示该数组首元素的地址
但&arr也是表示首元素的地址吗?
为此我们需要设计一个实验
如果&arr+1表示的是第二个元素的地址(即&arr+1和arr+1表示的内容相同) ———— 说明&arr是首元素地址
如果&arr+1与arr+1(或&arr[0]+1)不等价 ———— 说明&arr不是首元素地址
很显然,&arr并不是首元素的地址,下面是对于这种情况的解释
由图可见,&arr+1实际上是跳过了所有元素的地址,而arr+1只是跳过了一个元素
得出结论:&arr实际上是整个元素的地址,但是为了节约空间,只取该数组第一个元素的地址来代表整个元素的地址
2)数组指针的使用
数组指针是用于储存数组地址的指针变量
例如,我们可以用一个数组指针来控制二维数组
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int(*parr)[5] = arr;
return 0;
}
值得注意的是,(*parr)中的*并不是解引用符号,只用于标明parr是一个指针变量
在一个二维数组中,元素名代表首元素的地址(即第一行元素的地址)
第一行元素可以看作一个数组,故它可以被储存到数组指针中去
那么再结合一下解引用符合以及指针变量的加减运算,我们可以很容易找到二维数组中某一行的地址
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int(*parr)[5] = arr;
//*(parr+0)表示第一行元素的地址
//*(parr+1)表示第二行元素的地址
//*(parr+2)表示第三行元素的地址
return 0;
}
刚刚已经提到了,数组的地址等价于数组首元素的地址,通过上述的解引用方法,可以找到每一个数组的第一个元素
接下来,我们要再去访问某一个数组中的某号元素,这就需要再对解引用方法进行一些修改
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} };
int(*parr)[5] = arr;
//*(*(parr + 1)+4)表示1号数组的4号元素 —— 10
//*(*(parr + 0)+3)表示0号数组的3号元素 —— 4
//*(*(parr + 2)+1)表示2号数组的1号元素 —— 12
代码执行结果如下
通过这个例子,数组指针的使用就很清晰了
实际上还有数组指针数组,就是用于存放数组指针的数组,原理相同