三 数组指针
3.1 数组指针的定义
数组指针落脚点在最后两个字,所以数组指针就是一个指针。
字符指针,指向字符的指针;
整形指针,指向整形的指针;
数组指针,指向数组的指针。
1. #include <stdio.h> 2. int main() 3. { 4. int a = 3; 5. int* p = &a; 6. int* arr[10];//指针数组 arr和[10]结合,arr是一个数组 7. int* arr[10];//指针数组 arr和[10]结合 8. int(*arr)[10];//数组指针 ,arr和*结合,arr是一个指针,数组指针,int类型 数组指针,指向数组的指针 9. return 0; 10. }
int (*p)[10];
//解释:p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合
3.2 &数组名VS数组名
int arr[10];
arr 和 &arr 分别是啥?我们知道arr是数组名,数组名表示数组首元素的地址。那&arr数组名到底是啥?
代码1展示:
1. #include <stdio.h> 2. int main() 3. { 4. int arr[10]; 5. printf("%p\n", arr); 6. printf("%p\n", &arr); 7. return 0; 8. }
代码2展示:
1. #include <stdio.h> 2. int main() 3. { 4. int arr[10]; 5. printf("%p\n", arr); 6. printf("%p\n", &arr); 7. printf("%p\n", arr + 1); 8. printf("%p\n", &arr + 1); 9. return 0; 10. }
比较代码1和代码2:根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。
实际上: &arr 表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
arr是数组首元素(第一个元素)地址,如果存放在一个指针变量里,就是int*类型 arr+1 是第二个元素的地址 ,如果存放在一个指针变量里,就是整形指针类型
本例中 &arr 的类型是: int(*)[10] , 是一种数组指针类型(int a;去掉变量就是类型)
数组的指针存放起来,就是放在数组指针里。
数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.
(*p)说明是一个指针,(*p)[10]说明是一个数组,指向的数组里的元素是什么类型,int 类型, 数组的类型是int [10]
int* a;*说明a是一个指针。
3.3 数组指针的使用
数组指针指向的是数组,那数组指针中存放的应该是数组的地址
#include <stdio.h>
int main()
{
int arr[5];
int(*p)[5] = &arr;p的类型是int(*)[5]
int* paa[3];//指针数组,也是一个数组,数组里面的元素是int*类型
int* (*pp)[3] = &paa;//数组指针,pp的类型是int*(*)[3]
return 0;
}
指针数组的用途:
打印数组元素地址
代码1展示:
1. #include <stdio.h> 2. int main() 3. { 4. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 5. int* p = arr; 6. int i = 0; 7. for (i = 0; i < 10; i++) 8. { 9. printf("%d ", *(p+i)); 10. } 11. return 0; 12. }
打印结果:1 2 3 4 5 6 7 8 9 10
代码2展示:
1. #include <stdio.h> 2. int main() 3. { 4. int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; 5. int(*p)[10] = &arr; 6. int i = 0; 7. for (i = 0; i < 10; i++) 8. { 9. printf("%d ", *((*p) + i));p是&arr,*p是arr。 10. } 11. return 0; 12. }
打印结果:1 2 3 4 5 6 7 8 9 10
代码2这种方法不建议,数组指针我们一般用在二维数组中。
打印二维数组:
代码1展示:
1. #include <stdio.h> 2. void print(int arr[3][5], int a, int b) 3. { 4. int i = 0; 5. int j = 0; 6. for (i = 0; i < a; i++) 7. { 8. for (j = 0; j < b; j++) 9. { 10. printf("%d ", arr[i][j]); 11. } 12. printf("\n"); 13. } 14. } 15. int main() 16. { 17. int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} }; 18. print(arr, 3, 5); 19. return 0; 20. }
打印结果:
代码2展示:
二维数组在传参的时候,数组名也代表首元素地址,这个首元素指的是 第一行 ;首元素地址指的是第一行的地址
1. #include <stdio.h> 2. void print(int(*p)[5], int a, int b)//p+1就是第二行的地址,p+2就是第三行的地址 3. { 4. int i = 0; 5. int j = 0; 6. for (i = 0; i < a; i++) 7. { 8. for (j = 0; j < b; j++) 9. { 10. printf("%d ", *(*(p + i) + j));// *(p+i)拿到的是第i行的地址,相当于第i行的数组名,也就是第i行的首元素地址,也是第一个元素的地址 11. } 12. printf("\n"); 13. } 14. } 15. int main() 16. { 17. int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15} }; 18. print(arr, 3, 5); 19. return 0; 20. }
上图中的二维数组的首元素地址是 第一行的地址,传到函数里是int(*p)[5],
重点理解:
上述代码,p代表的是&第一行整个数组,p+i代表的是&第i行整个数组,*(p+i)代表的是第i行的数组名。数组首元素地址,数组首元素地址+i,指的是第i个元素。
1. #include <stdio.h> 2. int main() 3. { 4. int arr[5];//arr是一个整形数组,有5个元素,每一个元素都是int类型 5. int* parr1[10];//parr1是一个数组,数组有10个元素,每一个元素是int*类型 6. int(*parr2)[10];//parr2和*结合,说明parr2是一个指针,该指针指向一个数组,所以是一个数组指针 7. int(*parr3[10])[5];//把arr3[10],看做一个整体,指向数组为[5]的int的指针,这个指针是[10]. 8. //parr3和[]结合。说明parr3是一个数组,数组是10个元素,数组每一个元素的类型是int(*)[5],该类型的指针指向的数组有5个int类型的元素。 数组的类型是int*[5],也就是这个指针是数组,一共10个元素,每一个元素(指针)指向的是int[5] 9. return 0; 10. }
理解: parr3的类型是,int(*)[5],可以看做 int(*)[5] parr3,此时就是一个数组指针,但是上图是一个数组,那么就是10个数组指针放在了一起,是parr3[10]。