本章我们将从以下几点继续学习指针
目录:
1.数组名
2.二级指针
3.指针数组
一、数组名
1.数组名的理解
先说结论:数组名就是数组首元素的地址,但是有两个例外,一个是sizeof(数组名)另一个是&(数组名)
1.1数组名是数组首元素的地址
既然说数组名是首元素的地址,那么我们可以敲代码来实践一下看是不是如此。
#include<stdio.h>
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%p\n", &arr[0]);
printf("%d\n", arr);
return 0;
}
运行代码结果如下:
这里不相同是由于打印arr时使用的是%d而不是%p,而通过进制转换我们可以发现他们两是一样的。
当使用%p打印arr时也确实如此。
1.2sizeof(数组名)与&(数组名)
既然数组名表示的是数组首元素的地址,那么sizeof(数组名)与&(数组名)中的数组名表示的是什么呢?
#include<stdio.h>
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%d\n", sizeof(arr));
return 0;
}
运行结果:
可以看到,若在sizeof中,若数组名表示的是数组首元素的地址那么运行结果应当的4/8,但是这里的结果的40。而这里的40刚好是数组的大小(单位是字节),那么sizeof(数组名)中,数组名表示的应当是整个数组。
而&(数组名)也和sizeof(数组名)一样,表示的都是整个数组。
但是,当你好奇的去一一打印他们的地址的时候:
#include<stdio.h>
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("arr = %p\n", arr);
printf("&arr = %p\n", &arr);
return 0;
}
arr和&arr所打印出来的内容是一样的,这是怎么回事呢?既然说&(数组名)取出来的是整个数组,那么我们可以通过让他们两分别+1来看看是否还相同。
#include<stdio.h>
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
可以看到,arr与arr+1之间差了四个字节刚好是一个int型,而&arr与&arr+1之间差了整整40个字节,也就是10个int型,这也刚好是这一整个数组的大小。从这里也就验证出了&arr取出的是整个数组的地址。
2.使用指针的方式访问数组
我们都知道指针访问数组只需要拿到数组首元素的地址就可以顺藤摸瓜的找到其余元素,理解了数组名后我们就可以将代码进一步简化。
#include<stdio.h>
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = &arr[0]; //理解数组名前
int* p = arr; //理解数组名后
for(int i = 0; i < 10; i++) {
printf("%d ", *(p + i));
}
return 0;
}
这两种代码的效果都是一样的,只不过理解数组名后的方法更加简便。
在这里*(p+i)也可以表示成p[i],结果也是一样的。
我们知道,加法是支持交换律的,那我们可不可以大胆的猜想,可不可以写成i[p]呢?
可以看到,这种猜想是正确的,同理的其他数组也可以如此。
3.一维数组传参的本质
有如下代码:
#include<stdio.h>
void text(int arr[10]) {
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
}
int main() {
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
text(arr[10]);
return 0;
}
按照我们之前的理解,屏幕上打印的应该是10,可事实真的如此吗?
可以看到,结果跟我们的预期大相径庭。当我们知道数组名是数组首元素的地址后我们也就可以理解,传过去的其实是一个地址而不是数组,计算的是地址的大小相除,因此计算出来的结果也就不同了。
二、二级指针
当我们学习了一级指针后我们知道,一级指针保存的是变量的地址,那么我们可不可以套娃式的再创建一个指针来保存一级指针的地址呢?答案是可以的,而这个保存一级指针地址的指针,就是二级指针。
#include<stdio.h>
int main() {
int i = 10;
int* p1 = &i;
int** p2 = &p1;
return 0;
}
如上因为一级指针的类型是int*,那么二级指针的类型是int**,第二个*表示这是一个指针,第一个*表示所保存的地址的类型是int*型。而使用他时,第一次解引用拿到p1的值,第二次解引用拿到i的值。
三.指针数组
1.什么是指针数组
指针数组的形式:int* p[ ] , 我们都知道整形数组(int p[ ])表示的是一个数组里面保存的是整形的值。那么如上的指针数组我们也就可以理解为一个数组,里面保存的是int*类型的值。
#include<stdio.h>
int main() {
int i = 10;
int n = 20;
int u = 30;
int* p1 = &i;
int* p2 = &n;
int* p3 = &u;
int* arr[3] = { p1,p2,p3 };
return 0;
}
类似上面的,我们就创建了一个指针数组来保存p1 p2 p3 的内容,也可以跟其他类型的数组一样进行调用。
2.指针数组模拟二维数组
#include<stdio.h>
int main() {
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,7 };
int* arr[3] = { arr1, arr2, arr3 };
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
通过上述代码我们可以看到,通过指针数组,我们达到了二维数组的效果。这就是指针数组的一个作用。
(封面图源百度)