数组指针
通过对int* 类型、char* 类型的指针的理解,顾名思义,数组指针就是存放数组的地址的指针,指向的是数组的指针。图解:
怎么去定义一个数组指针?图解:
观察下面这段代码的输出结果。
#include <stdio.h>
int main()
{
int arr[4] = { 0 };
int* p1 = &arr[0];
int(*p2)[4] = &arr;
printf("p1=%p\n", p1);
printf("p2=%p\n", p2);
printf("\n");
printf("p1+1=%p\n", p1 + 1);
printf("p2+1=%p\n", p2 + 1);
return 0;
}
通过上图知道了p1+1和p2+1为什么不同。你会不会产生一个疑问,为什么p1和p2的值相等?p1是一个int类型元素的地址,而p2是一整个数组的地址。p1面对int类型4个内存单元(一个内存单元一个字节),只存了第一个内存单元的地址;p2面对一个数组16个内存单元,只存了第一个内存单元的地址。那么指针怎么做到解引用时正确地找到数据呢?这就体现了数据类型的重要性,当已知了起始内存单元地址和数据类型,就可以根据数据类型的大小在起始内存单元地址的基础上向后移动相应步长,找到该指针指向的数据的所有的内存单元。所以p1和p2都只用存对应数据起始内存单元的地址就够了,刚好他们对应的起始内存单元一样(都是arr数组的开头),所以他们值一样。
由此也可以得出:指针的大小是相对固定的,在64位平台下大小是8字节,32位平台下是4字节。
二维数组
如图,arr是数组指针,指向数组第一行元素,可以通过下面一段代码验证:
#include <stdio.h>
int main()
{
int arr[4][5] = { 0 };
printf("arr=%p\n", arr);
printf("arr+1=%p\n", arr + 1);
//通过结果可以发现arr和arr+1的值差等于一行数组元素的大小(20个字节)
}
关于数组名的总结:
数组名一般是数组首元素的地址,有两个例外。
- sizeof(数组名)此时数组名不表示首元素地址,表示整个数组,sizeof(数组名)计算的是整个数组的大小。
- &数组名 此时数组名不表示首元素地址,而是整个数组,取得是整个数组的地址,可以赋值给数组指针变量。
以上两个例外中的数组名是存粹的数组名,不参杂其他字符。
探究下面一段代码的输出:
#include <stdio.h>
int main()
{
int arr[4][5] = { 0 };
printf("sizeof(arr[0])=%d\n", sizeof(arr[0]));//1
printf("sizeof(arr[0]+1)=%d\n", sizeof(arr[0] + 1));//2
printf("sizeof(arr)=%d\n", sizeof(arr));//3
printf("sizeof(arr+1)=%d\n", sizeof(arr + 1));//4
return 0;
}
解析:
- arr[0]是数组第一行的数组名,同时单独在sizeof()中,所以表示整个第一行,计算的是第一行元素的大小,结果为20。
- arr[0] 是数组第一行的数组名,不是单独的在sizeof()中,所以表示首元素地址,加一后向后移动1*4个字节(移动后指向数组第一行第二个元素),因为arr[0]首元素地址是int*类型。加一之后还是地址,大小由平台决定4/8字节。
- arr是整个数组的数组名,同时单独在sizeof()中,所以表示整个数组,计算整个数组的大小,结果为80。
- arr是整个数组的数组名,不是单独在sizeof()中,所以表示首元素(第一行)的地址,arr的首元素地址类型是数组指针类型,所以向后移动1*20个字节(移动后指向数组第二行)。加一之后还是地址,大小由平台决定4/8字节。
数组指针在二维数组里的运用
二维数组传参:
分享一个小小习题:
这个题可以让你深刻体会指针!探究输出:
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}