二级指针和解引用
二级指针: 指向一级指针的指针, 二级指针是存储一级指针的地址
int **ppa = &pa, **ppb=&pb , **ppc =&pc;
printf("ppa=%d, ppb=%d, ppc=%d\n", ppa, ppb ,ppc);
二级指针解引用
*ppa <=> pa
*pa <=> b
printf("*ppa=%d, *ppb=%d, *ppc=%d\n", *ppa, *ppb, *ppc);
**ppa <=> *(*ppa) <=> *pa <=> b
printf("**ppa=%d, **ppb=%d, **ppc=%d\n", **ppa, **ppb, **ppc);
多级指针
三级指针
int*** pppa, ***pppc, ***pppb;
野指针
四级指针 : 应该存储三级指针的地址
a = (int)&ppa;
printf("a=%d\n", a);
int ****ppppa = &a;
printf("*ppppa=%d\n", *ppppa); //一次解引用=>三级指针 ppa的地址
printf("**ppppa=%d\n", **ppppa); //两次解引用=> ppa =>pa的地址
printf("****ppppa=%d\n", ****ppppa); //四次解引用=>数据 a
四级指针 *ppppa => 等价于三级指针 pppa
三级指针 *pppa => 等价于二级指针 ppa
指针的运算
- 指针 + 整数 = 指针
- 指针 - 整数 = 指针
- 指针 - 指针 = n单位(取决于数据类型)
指针之间也可以进行大小比较(>, <, >=, <=),但是在同一段连续的内存当中进行指针的运算,才有意义。【此外还有!= ,==】
数组
数组:一段连续的内存
int* arr[10] = {1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < 10;i++)
{
printf("%d, ", arr[i]);
}
printf("\n");
- 数组名是一个地址, 表示数组的首地址, 数组下标为0的元素的地址, arr[0]这个元素的地址,是个指针.
- 数组名是一个指针, 类型是什么? int型指针, int*型 , 取决于元素的类型。
- 数组名是一个常量,不能修改。
printf("arr=%d\n", arr); //假设第一个数字所在内存位置为10000
printf("arr + 1=%d\n", arr + 1); //内存位置不是10001,应该+4,10004
printf("arr + 2=%d\n", arr + 2);
...
printf("arr + 9=%d\n", arr + 9);
printf("arr + 10=%d\n", arr + 10);//属于越界了,不在初始化的范围之内
数组内指针的应用
指针 - 整数
int* parr = arr + 8;
printf("parr-9=%d\n", parr - 9); //越界了
指针 - 指针
printf("parr - arr=%d\n", parr- arr);
指针必须是一个变量,不能是常量
arr++;
arr--;
指针 ++
指针 - -
parr = arr;
parr++;
parr = parr + 1;
parr--;
parr = parr - 1;
printf("parr++=%d\n", parr);
数组和指针的关系
指针和一维数组的关系
printf("arr + 0=%d\n", arr + 0); //10000
printf("arr + 1=%d\n", arr + 1); //不是10001 ,应该+4
printf("arr + 2=%d\n", arr + 2);
//...
printf("arr + 9=%d\n", arr + 9);
printf("&arr[0]=%d\n", &arr[0]);
printf("&arr[1]=%d\n", &arr[1]);
//....
printf("&arr[9]=%d\n", &arr[9]);
/*
推导
arr + 0 <=> &arr[0]
arr + 1 <=> &arr[1]
arr + 2 <=> &arr[2]
...
arr + 9 <=> &arr[9]
*/
printf("*(arr + 0)=%d\n", *(arr + 0));
printf("*(arr + 1)=%d\n", *(arr + 1));
/*
推导
*(arr + 0) <=> arr[0]
*(arr + 1) <=> arr[1]
*(arr + 2) <=> arr[2]
....
*(arr + 9) <=> arr[9]
*/
for (int i = 0; i < 10;i++)
{
printf("%d, ", *(arr+i));
}
printf("\n\n\n");
问:在一段连读的内存中, 下标方式访问 和 间接访问 有什么区别?
答:下标访问可以和间接访问互相转化,用间接访问的方式效率还会更高
指针和二维数组的关系
int arr2[4][5] =
{
{1,2,3,4,5},
{6,7,8,9,10},
{11,12,13,14,15},
{16,17,18,19,20}
};
for (int i = 0; i < 4;i++)
{
for (int j = 0; j < 5;j++)
{
printf("%d, ", arr2[i][j]);
}
printf("\n");
}
printf("\n");
指针和一维数组的关系,同样适用于二维数组
问 arr2:二维数组的数组名,表示什么,类型是什么?
答:数组的首地址, 数组的下标为0的元素的地址, 也就是数组arr2[0]这个元素的地址
int arr2[4][5] = {{ 1, 2, 3, 4, 5 },{ 6, 7, 8, 9, 10 },{ 11, 12, 13, 14, 15 },{ 16, 17, 18, 19, 20 }};
把二位数组就看成 一维数组
arr2的类型是 一维数组的地址, 指向数组的指针,简称数组指针
int(*parr2)[5]; //parr2就表示指向数组的指针, 5是不能省略 int(*)[5]
printf("arr2 + 0=%d\n", arr2 + 0); //指向数组的指针
printf("arr2 + 1=%d\n", arr2 + 1); //
printf("arr2 + 3=%d\n", arr2 + 3);
printf("&arr2[0]=%d\n", &arr2[0]);
/*
推导
arr2 + 0 <=> &arr2[0]
arr2 + 1 <=> &arr2[1]
...
arr2 + 3 <=> &arr2[3]
再次推导:
*(arr2 + 0) <=> arr2[0]
*(arr2 + 1) <=> arr2[1]
...
*(arr2 + 3) <=> arr2[3]
再次推导
*(arr2 + 0) + 0 <=> arr2[0]+0 <=> &arr2[0][0]
*(arr2 + 0) + 1 <=> arr2[0]+1 <=> &arr2[0][1]
...
*(arr2 + 0) + 4 <=> arr2[0]+4 <=> &arr2[0][4]
*(arr2 + 1) + 4 <=> arr2[1]+4 <=> &arr2[1][4]
再次推导
*(*(arr2 + 0) + 0 ) <=> arr2[0][0]
*(*(arr2 + 0) + 1 ) <=> arr2[0][1]
...
*(*(arr2 + 1) + 0 ) <=> arr2[1][0]
*/
printf("*(arr2+0)=%d\n", *(arr2 + 0));
printf("arr2[0]=%d\n", arr2[0]); //arr2[0]一维数组的数组名 , arr2[0][0]的地址,类型:int*
printf("arr2[0] + 1=%d\n\n", arr2[0] + 1);
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d, ", (*(arr2 + i))[j] ); //(*(arr2 + i))
}
printf("\n");
}
printf("\n");
问:怎么定义一定一个指向三维数组的指针
int arr4[2][3][4] = {0};
int(*parr4)[3][4] = arr4;
指针和字符串数组的关系
char* str = "HelloWorld";
printf("%s\n", str); //打印整个字符串
printf("%d\n", "HelloWorld"[0]); //打印字符串首个字符的ACSII码 72
printf("%c\n", *("HelloWorld")); //打印字符串的首个字符"H"