在C语言中,指针是个神奇的东西,在window底层对指针的妙用无处不在。这里来总结数组针指的小结。在内存里面系统就是通过指针来寻找数组的每个元素的。
一、数组指针
1.一维数组:
数组指针:指向一个数组的指针。
int arr[]={1,2,3,4,5,6,7,8,9};
这里有个整形数组 arr,这里我们知道arr是数组名,也是地址首元素的地址。 那么能不能定义一个指针来指向这个数组并调用数组里面的元素呢。
其实是可以的。那么指针的类型是什么样的呢。
我们定义指针的时候:
int a=10;
int* p=&a;
printf("%d",*p);
这里p就是一个指向a地址的指针,取地址a (&a),取得的就是a的地址,解引用(*p)就是获得变量a上存的值。
这里可以总结一个小规律,一个变量取地址(&)就相当于原来的类型加上一个*,一个指针解(*)引用就相当原来的类型减一个*。
&a(取地址)的后获得的类型就是 int*,*p(解引用)后获得的类型是 int
那么对于数组来说也是一样的,把数组取地址(&arr),得到的类型是原类型加上一个*。
int (*arr)[];
//注意不是 int *arr[];
那么用来接受这个数组地址的类型也该是
int arr[10];
int (*p)[10]=&arr;
这里指针p是一个指针,指向一个数组,数组的返回类型是int类,所以p就叫数组指针,表达了整个数组。
PS:
int (*p1)[10];
int (*p2)[5];
这里 [ ] 里面10跟5的差别很大,是10的时候 p+1,跳过数组的10个元素,是5的时候则跳过5个元素。
void print(int (*p)[10],int sz) //这种写法属于绕远路,一般写的少
{
for(int i=0;i<sz;i++){
printf("%d ",*(*p+i));
}
}
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
print(&arr,sz);
指针p解引用(*p),原类型去掉一个*,就是 int p[10],这里p就代表数组名,数组名就是首元素的地址。所以解引用数组指针,就是数组首元素地址。
那么 首元素地址再加上一个数 i,就能取到第i个元素的地址 *(*p+i )
PS:这里 p,&arr,arr的值是一样的,代表的含义不同。
数组名:
1.sizeof(arr) ,这里arr表示的是整个数组。
2.&arr,这里也是表示取的整个数组的地址。
然后其他的数组名,都是表示数组首元素地址。
* :虽然地址首元素的地址跟整个数组的地址是一样的,但是 首元素的地址+1,则跳到下一个元素,整个数组的地址+1,则跳过整个数组。
2.二维数组:
对于二维数组来说,数组指针有又有些不同。
void print(int (*p)[3],int c,int r)
{
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
printf("%d ",*(*(p+i)+j);
}
}
}
int arr[3][3]={{1,2,3},{4,5,6},{7,8,9}};
print(arr,3,3);
//int (*p)[3][3],必须三行三列,才能接收二维数组arr
在二维数组中 arr 代表的是第一行的地址,相当于*(arr+0)、arr[0]。也叫行指针。
会发现这里 int arr[3][3],跟上面的 int (*p)[3] 很相似。所以这里的 arr也可以理解为一个数组指针指向数组的第一行。
&arr[0][0],*(arr+0)+0,才是第一个元素的地址(地址是一样的,意义不同)。
按照上面,arr取地址(&arr),原类型加一个*,int (*arr)[3][3],还是要注意不是int* arr[3][3],那么用来接收指向二维数组的指针的类型就是,int(*p)[3][3]。
解引用p (*p) ,原类型减去一个*,就是 int p[3][3],这里的p代表的是数组第一行的地址, *(p+i),就获得的是数组第i行首元素的地址,再解引用*(*p),原类型再减去一个*就是int p[3],这里的p就是首元素地址了,*(*(p+i)+j)获得的就是第几行第几个元素。
这里有多中写法:1.p[ i ][ j ] 2.*( p + i ) [ j ]
PS:这里的p[3][3],元素的个数必须跟二维数组一样。
PS:理解去*,加*的时候,*是从变量名最近的*号开始的,也就是从最里面的*开始算。
二、指针数组
那么为什么数组指针不能写成 int*arr[10],这种形式呢。
因为这种数组代表的意义不同,此数组叫指针数组,意思是一个数组arr,有10个元素,每个元素的类型是 int* 的指针。
int a1,a2,a3;
int *p1=&a1;
int *p2=&a2;
int *p3=&a3;
int *arr[3]={p1,p2,p3};
指针数组也可以定义一个指向数组的指针:
int* (*arr)[3]; 二维数组: int* (*arr)[3][3];
当然不管是在这里还是在上面,都还可以进行无限的套娃(加*),不过意义已经不大而且几乎使用不到,并且非常复杂。
对于指针数组来说,则就可以使用 int * *arr(二级指针)来接受数组。
void test(int* *p){ }
int *arr[3];
test(arr) //不是&arr
但是为什么某些编译器或者网站则使用 int* * arr来接受二维数组呢?
其实它们不是标准的二维数组,只是二维数组的一个模拟
总结
光是在数组里面的指针的意义和用法都多种多样,要熟练指针,多练习和运用。