1.数组名的理解
int arr[10] = { 0 };
int* p = &arr[0];
int* p = arr;
当我们用指针访问数组时,会有上面俩种写法。其实&arr[0]是拿到数组中的第一个元素的地址;大家要知道数组名本来就是地址,而且是数组首元素的地址。
通过输出结果,我们发现数组名和数组首元素打印出来的结果是一样的,数组名就是数组首元素(第一个元素)的地址 。
其实数组名就是数组首元素(第一个元素)是对的,但是有俩个例外:
- sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节
- &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的)
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d\n", sizeof(arr));
return 0;
}
输出的结果是:40。指针的大小根据编译器是4/8字节,而这里就是例外。
int arr[10] = { 0 };
printf(" &arr[0] = %p\n", &arr[0]); //指针类型是int*
printf(" &arr[0] + 1 = %p\n", &arr[0] + 1);printf("arr = %p\n", arr); //指针类型是int*
printf("arr + 1 = %p\n", arr + 1);printf("&arr = %p\n", &arr); //这个类型呢?
printf("&arr + 1 = %p\n", &arr + 1);
输出结果:
这⾥我们发现&arr[0]和&arr[0]+1相差4个字节,arr和arr+1 相差4个字节,是因为&arr[0] 和 arr 都是 ⾸元素的地址,+1就是跳过⼀个元素。而它们的类型都是int* ,大家可以理解为指针变量只能存储一个地址。
但是&arr 和 &arr+1相差40个字节(如图是十六进制,差28,转成10进制是40),这就是因为&arr是数组的地址,+1 操作是跳过整个数组的。而它的类型是什么呢?大家可以思考一下,之后为大家解答。
总的来说:
指针类型决定了指针+-整数时的步长
2.指针访问数组
有了前面的知识作为铺垫,再结合数组的特点,我们可以使用指针访问数组。
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
int i = 0;
//输入
for (i = 0; i < sz; i++)
{
scanf("%10d", p + i); //也可以写成arr + i
}
//打印
for (i = 0; i < sz; i++)
{
//printf("%d ", *(p + i));
//printf("%d ", *(arr + i));
//printf("%d ", arr[i]);
printf("%d ", p[i]);
}
return 0;
}
这个代码搞明⽩后,我们再试⼀下,如果我们再分析⼀下,数组名arr是数组⾸元素的地址,可以赋值 给p,其实数组名arr和p在这⾥是等价的。
当我们用数组的内容arr[i]打印各个元素时,数组元素的访问在编译器处理的时候,也是转换成首元素地址+偏移量求出元素地址,然后再解引用访问。
结论:arr[i] ---> *(arr + i) ---> *(i + arr) -->i[arr],这种大家需要了解一下,当然在写代码中写成i[arr]是不好的,不建议大家使用,但是大家需要知道这种写法。
3.二级指针
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪⾥? 这就是⼆级指针 。
int a = 100;
int* pa = &a;
int** ppa = &p; //pa就是二级指针变量,二级指针就是用来存放一级指针变量的
printf("%d ", **pa);
首先,&a指的是取地址,pa是指针变量,而*指的是它是指针变量,int指的是指向的对象a是int类型。 接下来看二级指针,ppa是二级指针变量,*指的是它是指针变量,int*指的是指向的对象p的类型是int*。
如图,因为创建变量的本质上是在内存中开辟一块新的地址。
当然了,二级指针也能解引用运算
**ppa = 30;
///等价于*pa = 30;
//等价于a = 30;
**ppa 先通过 *ppa 找到 pa ,然后对 pa 进⾏解引⽤操作: *pa ,那找到的是 a .
4. 指针数组
大家来思考一下指针数组是什么?是指针?还是数组?
我们先来类比一下,整型数组,是存放整型的数组,字符数组是存放字符的数组。
那指针数组呢?我们可以得知是存放指针的数组。
#include <stdio.h>
int main()
{
int a = 20;
int b = 11;
int c = 12;
int* arr[] = { &a, &b, &c }; //指针数组
int i = 0;
//打印
for (i = 0; i < 3; i++)
{
printf("%d ", *(arr[i]));
}
return 0;
}
指针数组模拟二维数组
#include <stdio.h>
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[6] = { 2,3,4,5,6 };
int arr3[5] = { 3,4,5,6,8 };
int* parr[]= { arr1, arr2, arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
//arr[i]
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", p[i][j]);
}
printf("\n");
}
return 0;
}
parr[i]是访问parr数组的元素,parr[i]找到的数组元素指向了整型⼀维数组,parr[i][j]就是整型⼀维数 组中的元素。
上述的代码模拟出⼆维数组的效果,实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的。