四、指针运算
指针的运算包括指针+-整数,如p++;和指针+-指针
1.指针加减整数
#include<stdio.h> int main() { int arr[5] = {1,2,3,4,5};//创建并初始化数组 //利用指针加减整数打印数组 int* p = arr;//定义和初始化指针 int i = 0; for (i = 0; i < 5; i++) { printf("%d ",*(p+i)); } //*(p+i)可以写成*p++,但是后者的p会发生变化 return 0; }
运行结果:
第一次循环:i=0,所以p+i=p,然后解引用p找的是arr[0],并打印。
p指向的地址是数组首元素的地址;p+i是数组下标为i的元素的地址;(p+i)跳过i*sizeof(int)个字节。
总结:指针加减整数,不是跳过整数个地址,而是跳过整数*sizeof(指针所指向的数据类型)个地址。
标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较
2.指针-指针
两个指针相减就是地址之间的运算,一般在数组中出现,因为;
指针减指针的前提条件:两个指针必须指向同一块区域,且指针类型相同。
举例:
#include<stdio.h> int main() { int arr[10] = {0}; printf("%d\n",&arr[9]-&arr[0]); printf("%d\n",&arr[0]-&arr[9]); //最后一个元素的地址-首元素地址 return 0; }
运行结果
总结:指针-指针插值的绝对值,是指针和指针之间的元素个数。
五、指针与数组的运用
因为数组名是首元素的地址,所以可以通过首元素地址访问该数组,也就可以将地址赋值给指针变量,再用指针去访问数组,这就是他们的关系。
知识点:数组名就是表示数组首元素的地址,但是有两个例外。
1.sizeof(数组名),数组名单独放在括号内部,数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2.&数组名,数组名表示整个数组,取出的是数组的地址,数组的地址和数组首元素的地址在数值上是一样的,但是类型和意义不一样的,体现在地址+1上面。
(该知识点已经在数组的章节介绍过,属于数组的知识点)
例子说明:
#include <stdio.h> int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,0 }; int* p = arr; //指针存放数组首元素的地址 int i = 0; int sz = sizeof(arr) / sizeof(arr[0]); for (i = 0; i < sz; i++) { printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i); } return 0; }
运行结果:
通过指针来访问数组是可行的,因为指针所指向的地址是跟数组匹配的。
总结:因为数组名是地址的原因,所以可以和指针很好的配合。
六、二级指针
二级指针也是指针,不过二级指针里面存放的地址是一级指针的地址,依次类推,三级指针存放二级指针的地址……
#include <stdio.h> int main() { int a = 10; int* p = &a; //*说明p是指针,int说明p指向的类型是int //p是一级指针,存放变量a的地址 int* * pp = &p; //第一个*说明pp是指针,int*说明pp指向的p变量类型是int* //pp是二级指针 return 0; }
里面的文字解析很重要
七、指针数组
指针数组本质是数组,只不过里面存放的类型是指针类型。就像字符数组里面存放的是字符一样。
如:
char* arr1[5];//存放字符指针的数组 int* arr2[6];
举例:
#include<stdio.h> int main() { int arr1[3] = {1,2,3}; int arr2[3] = {4,5,6}; int arr3[3] = { 7,8,9 }; //arr就是指针数组 int* arr[3] = { arr1,arr2,arr3 }; //可以模拟二维数组 int i = 0; for (i=0;i<3;i++) { int j = 0; for (j=0;j<3;j++) { printf("%d ",arr[i][j]); //i=0,访问arr1 } printf("\n"); } return 0; }
运行结果:
指针数组在初级C语言不会详细介绍,在进阶C语言我们会进一步探讨。