一、数组指针
从名字上来说,数组指针的本质是一个指针,其指针指向一个数组,我们先举个数组指针的例子:
int (*p)[4];
我们都知道()括号运算符的优先级是最高的,变量描述符p使用*来进行修饰,表明p是一个指针。后面的[]描述的是一个数组,有4个元素。int表明了数组的元素是Int类型的数据。
为什么说数组指针本质是一个指针呢,我们以下面一段测试程序进行说明分析:
#include <stdio.h>
int main () {
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*p)[4];
int i;
p = a; //由于p是一个指针,因此可以将数组的首地址赋值给它
for(i = 0; i < 3; i++)
{
printf("%3d",(*p)[0]);
p++; //指针的特性,可以使用++运算符,P的增量是4个int
}
printf("\n");
return 0;
}
结果如下:
从上面的测试代码中可以看到,p的本质是一个指针,可以直接使用指针的方式进行操作,指针使用++运算符时,其值增加一个储存单位,在这里储存单位是一个有4个int类型元素数组。int (*p)[4] -- 所声明的变量p可以看做是一个二维数组,其行数是不确定的,其列数是固定为4.
另一个问题是在上述程序中,我们获取指针的值使用的是(*p)[0]的方式去获取每行(每个一维数组)的首元素的值,为什么要这样写呢?因为p是一个指针,指向的是一个数组,也就是说p所在地址所存的值是是所指向数组的首地址,因此(*p)才是数组的地址,后面就是访问数组的方式。
从另一个方面 -- p的空间大小,也可以说明p是一个指针:
#include <stdio.h>
int main () {
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int (*p)[4];
int i;
p = a; //由于p是一个指针,因此可以将数组的首地址赋值给它
for(i = 0; i < 3; i++)
{
printf("%3d",(*p)[0]);
p++;
}
printf("\n");
printf("sizeof(int *) = %zd,sizeof(p) = %zd\n",sizeof(int *),sizeof(p));
return 0;
}
可以看到,一个int *的大小是8bytes,而p的大小也是8bytes,说明p也是一个int *类型。
二、指针数组
与数组指针不同,指针数组是本质是一个数组,数组的元素是指针。同样的,我们先来以一个典型的声明例子进行说明:
int *p[3];
[]方括号的优先级高于*,因此p是一个指针,而指针的元素是int *类型,即p是一个有3个元素的数组,数组的内容是整型指针。
我们也写一个测试程序进行说明分析:
#include <stdio.h>
int main () {
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *p[4];
int i;
p = a; //由于此时p不是一个指针而是一个数组,因此不能这样赋值
for(i = 0; i < 3; i++)
{
printf("%3d",(*p)[0]);
p++;
}
printf("\n");
printf("sizeof(int *) = %zd,sizeof(p) = %zd\n",sizeof(int *),sizeof(p));
return 0;
}
当我们像数组指针一样去将数组名赋值给p,此时编译器就会报错:
而p也不能使用++运算符了。
正确的使用方法:
#include <stdio.h>
int main () {
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int *p[3];
int i;
for(i = 0; i < 3; i++)
{
p[i] = a[i]; //p本质是一个数组,我们用数组的操作方式去操作它
printf("%3d",*p[i]);
}
printf("\n");
printf("sizeof(int *) = %zd,sizeof(p) = %zd\n",sizeof(int *),sizeof(p));
return 0;
}
我们可以看到p所占的空间是24bytes = 3 * 8bytes,即p储存着3个int *数据,p[i]是一个指针,我们用指针的方式去访问它。