指针数组和数组指针
大家看到这两个词,是不是有点头大呢?
别说你们。我看着都有点头大,虽然知道什么时数组,什么是指针,但是他们结合起来是个什么玩意呢?
相比大家对数组和指针应该耳熟能详了吧,那么有这两个基础了,咱们再来看今天我们要讨论的指针数组和数组指针。
指针数组
以前我们在学习数组的时候,肯定听说过整型数组,字符数组,浮点型数组之类的,既然数组其中可以放字符、整数、浮点数,那么可不可以放其他的东西呢?答案当然是可以的,比如放结构体,就是结构体数组,那么如果行的是指针呢?是不是就成了我们今天主要说的指针数组了呢?答案是肯定的。
那么我们的指针数组离如何去定义呢?我们都知道普通的整型数组,字符数组,浮点型数组是这样的
int Arr[10];
char Arr[10];
double Arr[10];
struct Student FirstClass[20];
他们内部分别存的是整型、字符型、浮点型的数组,他们的类型就是其相对应的类型,那么我们要存一个整型指针呢?是不是数组的类型就是整型指针类型的呢?所以我们的指针数组是这样定义的:
int *Arr[10]; //(一级)整型指针数组
double *Arr[10];//(一级)字符指针数组
float **Arr[10];//二级单精度指针数组
那么问题来了,指针数组到底时指针还是数组呢?
答案是数组,它和我们一般见到的数组并没有的区别,只是它的内部存储的是一个指针而已。
数组指针
以前我们在学习指针的时候,是不是有整型指针,字符指针,还有浮点数指针呢?他们代表的意思都是指向该类型数据地址最小的字节,再根据其内部数据类型的大小就可以得到该数据。那么我们定义一个结构体变量,那么他有地址吗?答案是肯定的,所以指向结构体的指针就叫做结构体指针。那么我们的数组有地址吗?答案也是有,那么指向数组的指针就叫做数组指针。
那么如何定义呢?整型指针、字符指针、浮点数指针相信大家都会吧,我们举几个例子:
int *pint;
float *pfloat;
char *pchar;
struct Student *Stu;
他们都是指向其对应类型的指针,那么我们的数组指针呢?数组指针定义比较特殊,我们就直接开门见山:
int (*p)[10]; //指向大小为10的整型数组指针
float (*p)[20]; //指向大小为20的浮点型数组指针
int (*p[10])[20]; //那么这个呢?这个我们接下来会说
那么问题又来了,数组指针是数组还是指针呢?
答案是指针,它是一个指向数组的指针。
那么这两个这么像,而且又这么绕,我们之后见到该辨别呢?下面我们就说以下他们两个的辨别。
如何辨别指针数组和数组指针
int *p[10];
int (*p)[10];
int (*p[10])[20];
大家还能分清楚这前两个吗?很多人会说能,但是第三个呢,到底是指针数组还是数组指针呢?
这下就蒙圈了吧:/手动得意 我们来看一看,我们可以把他们分为四部分来看
int
*
p
[10]
第一部分是类型,第二部分是星号,第三部分是变量名,第四部分数数组的大小
那么 我们的变量名 p 是优先和谁结合呢?是 * 号 , 还是我们的 [ ] 中括号呢?
根据我们的优先级表可以得出 [ ] 中括号的优先级要高于 * 星号的 ,所以我们的变量名 p 是先和 [ ] 结合形成数组,所以他就是一个数组,然后在和 int * 结合 ,这就是我们的指针数组。
那么我们如果给星号 * 和 p 变量名带上圆括号呢?那么我们的 p 就先会和 * 进行结合变成一个指针,所以他就是一个指针,再和int [10] 结合变成一个指向大小为10的整型数组的指针。
所以归根结底,看的是变量名与哪一个先结合,先和 [ ] 结合,它就是数组;先和 * 结合 他就是一个指针。
注:数组的类型是由其基本类型加上其大小组成的,两个是强相关的。比如 int Arr[10] ,那么这个数组的类型其实就是 int [ 10 ],读者这里要注意。
那么大家知道,对指针进行++操作,就是给它加上其类型的大小。那么对我们的数组指针而言呢?
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr); //arr 为数组首元素的地址 指向其内部元素的指针
printf("&arr= %p\n", &arr); //&arr 为整个数组的地址 指向整个数组的指针
printf("arr+1 = %p\n", arr+1);
printf("&arr+1= %p\n", &arr+1);
return 0;
}
所以对我们数组指针而言,对它++就是相当于跳过了整个数组。
二维数组传参
相信我们对一维数组传参已经很清楚了;那么二维数组是怎样传参的呢?我们来看一段代码:
int Print1(int Arr[10][20], int row , int col){}
int Print2(int Arr[][20], int row , int col){}
int Print3(int (*Arr)[20], int row , int col){}
int main(){
int Arr[10][20] = { 0 };
Print1(Arr,10,20);
Print2(Arr,10,20);
Print3(Arr,10,20);
return 0;
}
请问,以上三种传参方式,那个对,那个不对呢?
其实以上三种传参方式都是对的,而且都是一样的,但是为什么呢?
大家都知道数组在传参时,或发生降维,降维成指针。所以第一种传参会降维,降维成指向其内部元素类型的指针,即就是指向一个大小为20的整型一维数组的指针,降维后其实就是 int (*Arr)[20],是不是和第三个一模一样呢?
我们知道数组在传参时第一维可以省略,那么为什么可以省略呢?我们可以看一下以下代码:
void Fun1(int Arr[100], int line){}
void Fun2(int *Arr, int line){}
int main(){
int Arr[10];
Fun(Arr,10);
}
大家在编译是会发现没有任何问题,是不是很奇怪?就是因为数组在传参时会降维,降维成指向其内部元素类型的指针,所以形参哪里填多少都没有关系,但是在传参时一定要把它的个各维的大小传进去,才能正确的访问其内部元素。
所以我们在进行数组传参时,我们可以有两种方式可以使用,可以根据个人喜好来进行选择
总结
今天我们主要讨论了数组指针和指针数组,今天主要是对其基本的概念进行讨论,到底如何去用,在什么场景下使用,我们以后再做以讨论。
由于本人才疏学浅,若有疏忽还望不吝赐教。
@YeLing0119