4.指针变量的运算
4.1指针+-整数
#include<stdio.h> int main() { int values[6] = {0}; int *vp = &values[0]; //指针+-整数;指针的关系运算 while (vp < &values[6]) { *vp++ = 1; } return 0; }
*vp++ = 1,++ 是一个后置。所以 vp 先和 * 结合找到 vp 指向的地址空间,将里面的内容改为1,然后再 vp++ ,指向下一个地址。 *vp++ = 1;相当于 *vp = 1;vp += 1;
4.2指针-指针
#include<stdio.h> int main() { int arr[7] = { 0 }; int* begin = arr;//数组名代表第一个元素下标地址 int* end = &arr[6]; printf("%d", end - begin); return 0; }
指针 - 指针 = 它们中间元素的个数
begin 指针变量指向的是数组第一个元素的地址,数组中每个元素的类型都为 int 型占4个字节大小,每个元素的地址都为4个字节中第一个字节的地址,那 &arr[6] 则是最后一个元素所占的4个字节的第一个字节的地址,所以我们 end - begin = 6
4.3指针的关系运算
#include<stdio.h> int main() { int arr[7] = { 0 }; for (int* vp = &arr[6]; vp >= &arr[0]; vp--) { *vp = 1; } return 0; }
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行
标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较
5.指针变量和数组
首先我们来看一个例子:
从上面的代码运行结果中我们可以看出:数组名跟数组首元素的地址是一样的
那我们就可以得出一个结论:数组名表示数组首元素地址
那我们再看两个例子:
例1:
当数组名 和 数组首元素放在 sizeof 中求大小时,却不同了。数组名放在 sizeof 中求大小时,求的是整个数组的大小。而数组首元素放在 sizeof 中求大小时,求的是首元素的大小。
例2:
arr 表示数组首元素地址,&arr 表示整个数组的地址,但是它们打印出来的结果是一样的地址。但是通过给它们分别 +1 就能看出区别,arr + 1 跳过的是一个元素大小,而 &arr + 1 跳过的是整个数组的大小
结论:sizeof(数组名)和 &数组名 表示整个数组大小,其余的 数组名 都表示数组首元素的地址
既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问数组就成为可能。
所以 p+i 其实计算的是数组 arr 下标为i的地址 ,那我们就可以直接通过指针来访问数组。
如下:
#include<stdio.h> int main() { char arr[6] = { 'a', 'b', 'c', 'd', 'e', 'f' }; char* p = arr; //指针存放数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i<sz; i++) { printf("%c\n", *(p + i)); } return 0; }
打印的时候也可以写成:printf("%c\n", p[i])。 我们知道 arr 表示数组首元素地址,arr[0] 表示数组第一个元素,那 p 指针变量中也是 arr 数组中首元素地址,那它同样 p[0] 表示数组第一个元素地址。
6.二级指针
二级指针:指针变量也是变量,那它也就有地址,存放一级指针地址的指针就是二级指针。
注:存放一级指针地址的指针称二级指针,存放二级指针地址的指针称三级指针...
把 a 的地址放在 p 中,p 就是一级指针。把 p 的地址放在 pp 中,pp 就是二级指针
对二级指针的运算:
*pp 通过对 pp 中的地址进行解引用,这样找到的是 p , *pp 其实访问的就是 p
**pp 先通过 *pp 找到 p ,然后对 p 进行解引用操作: *p ,那找到的是 a .