指针数组和数组指针一直是初学者的心头大患,看见就得避着走,接下来教大家5分钟理解他们:
int a[5][5] = {1, 2, 3, 4, 5};
int (*p)[5];
p = &a[0];
printf("%d", p[0][1]);
int a[5][5] = {1, 2, 3, 4, 5};
int *p[5];
p[0] = a[0];
printf("%d", p[0][1]);
首先大家分得清哪个是指针数组,哪个是数组指针吗?
先公布答案,第一个是数组指针,第二个是指针数组;
为何呢?因为第一部分括号优先级最高,*和p组合,意味着p是一个指针,而第二个*是和int组合,可以看为int *类型,所以第一部分可以写成 int[5] (*p); 第二部分写成 int* p[5];那现在去看,是不是第一部分是一个指针指向一个5个长度的数组,所以下面p指向二维数组a的第一行5长度的数组,注意是指向的这个整体并不是第一行的首地址,因为第一个首地址是一个int*类型,而要加上&让a[0]成为指向第一行的整体地址,那么p就指向了二维数组的第一行一维数组,p[0]就代表第一行整体,p[1]则是第二行,因为p指针是指向一个int[5]是一个5个长度的一维数组整体,所以p+1,地址不是加了一个int字节而是5个int字节;
然后说第二部分,int* p[5];这是一个指针数组,因为它只是一个存了5个int*类型的数组,所以让p[0]存a[0],也就是二维数组a的第一行数组的首地址(这里你应该就明白为什么在上面我要强调给数组指针赋值要加上&,而这里不用,因为数组名在运算时会退化为数组首地址,也就是a[0]代表的是a[0][0]的地址,而不是5个单位长度的数组整体,所以上面加上了&,让其指向整体),所以此时p[0]就代表着int* p[5]里面的第一个元素,也就是我们刚刚存储的二维数组a的首行元素的首地址,所以p[0][1]则是首行元素地址+1,所以是首行第二个元素,那么请你思考p[1][1]是否代表第二行第二个元素呢?
答案显然是不是的因为p代表的是数组,而p[1]根本没有存储元素所以p[1]没有指向第二行首地址,只有赋值过p[1]=a[1];才可以。
那么请你思考第一部分的数组指针p[1][1]是否代表第二行第二个元素呢? 答案是是的,请你思考为什么?欢迎评论区讨论。
下面是简化后的笔记:
指针数组与数组指针详解
基本概念
- 数组指针:指向数组的指针。
- 指针数组:数组的每个元素都是指针。
示例解析
数组指针示例
int a[5][5] = {1, 2, 3, 4, 5};
int (*p)[5];
p = &a[0];
printf("%d", p[0][1]);
- 解析:
int (*p)[5]
定义了一个指针变量p
,它可以指向一个含有5个整数的数组。p = &a[0];
让p
指向a
的首行(即a[0]
,整个一维数组)。p[0][1]
访问的是p
指向的数组中的第二个元素,即a[0][1]
。
指针数组示例
int a[5][5] = {1, 2, 3, 4, 5};
int *p[5];
p[0] = a[0];
printf("%d", p[0][1]);
- 解析:
int *p[5]
定义了一个包含5个整型指针的数组p
。p[0] = a[0];
将a
的首行首地址赋给了p[0]
,p[0]
现在指向a[0][0]
。p[0][1]
访问的是通过p[0]
指向的数组中的第二个元素,即a[0][1]
。
关键区别
- 数组指针指向的是整个一维数组,因此
p+1
跳过的是整个数组的大小(5个整数的大小)。 - 指针数组的每个元素是独立的指针,它们分别指向不同的位置,需要显式地将这些指针初始化为指向数组的不同行或列。
进一步理解
- 在数组指针中,
p[1][1]
确实代表第二行的第二个元素,这是因为p
指向整个数组,所以p[1]
自然指向数组的第二行。 - 在指针数组中,
p[1][1]
不直接代表第二行的第二个元素,除非p[1]
被显式初始化为指向第二行的首地址。
结论
理解指针数组和数组指针的关键在于明确它们各自指向的对象以及如何使用它们访问数据。希望这份整理能帮助你更好地掌握这两个概念!