本篇将先初步介绍指针的各种有关知识,然后再讲解指针与数组、指针与函数等
目录
首先要清楚一点,指针和内存是不可分割的,所以我们要先对内存有一定了解才能学懂指针。
内存
内存被划分为一个个内存单元,大小取一个字节,我们可以这样理解:把内存看作一个个房间,每个房间都有一个地址编号并且每个房间大小为一个字节
对于编号、地址和指针的关系有:内存单元的编号 == 地址 == 指针
而我们创建一个变量时就是把这个变量放进这个房间(地址)里,而我们指针就是获取这个地址,以对它进行各种操作
指针基本有关知识
首先在正式讲解指针前先将三个注意点:
- 指针变量里放的是地址
- 指针变量也是变量,拥有自己的地址
- *p 是 p 指向的空间
指针有关操作符:
&:取地址操作符,创建变量就是向内存申请空间,并放入该空间地址, & 可以取出这个地址的编号
*:解引用操作符,将指针(地址)解开,获得其里面的数据,例:
int a = 10; //创建一个变量 a,并赋值 10
int* p = &a; //取出 a 的地址并给予指针 p
printf("%d", *p) //把指针 p 解开,获得 p 所存的地址中所装的 变量 a,此行就会打印 10
指针变量的定义:
数据类型 * 指针变量名
其中“数据类型”是此指针所指向的数据的类型(也就是指针所存的地址里装的变量的类型),指针变量名就是此指针的变量名
例:
int * a //变量名为 a,* 说明这是一个指针,此指针指向的数据的类型为 int
char * b //变量名为 b,* 说明这是一个指针,此指针指向的数据的类型为 char
char (*arr) [10] //变量名为 arr,(*) 说明这是一个指针,此指针指向的数据的类型为 char [10](一个数组,元素为 char 类型,元素个数有十个)
注意:指针变量的类型是非常重要的(后面会讲),对于一个指针变量的类型,我们可以这样理解:如上例子:变量 a 的类型为 int*,变量 b 的类型为 char* ,变量 arr 的类型为 char [*] [10],指针变量与其他变量没有区别,只不过类型是 int*、char* 等
指针变量的大小:
因为指针就是地址,所以指针的大小就是地址的大小,在 32 位机器中地址有 32 个比特位,一共就是 4 个字节的空间,同理 64 位机器 8 个字节
指针变量类型的意义:
指针所存的都是地址,可指针的类型与其的使用有莫大的关系
1.指针类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节),指针所指向数据类型有多大,一次操作时就操作多少字节,例:
2.指针的类型决定了指针向前向后走一步有多大距离(偏移量),这个知识和指针的整数加减运算有关,举个例子:
注意: 图中我箭头所指是以空间开头为准,如果箭头指向空间中央也是表示指向这个空间,这个看个人习惯理解
指针运算:
1.指针加减整数 :指针加减几,就向右(+)/左(-)偏移 指针所指数据类型大小 * 加减的数字,例:int* p1 + 1 表示地址向右偏移 4 个字节,char* p2 - 3 表示地址向左偏移 3 个字节
2.指针减指针:指针减指针的运算结果的绝对值为两指针间相差的元素个数(此运算方式如同天数的运算,1 日到 5 日相差 4 天,从 1 日开始到 4 日结束(5 日开始))
注意:此运算只能在同一块空间使用
例:
int arr[10] = { 0 }; //创建一个整型数组
arr + 1 - &arr + 1 = -10; //arr 表示数组第二个元素地址,&arr + 1 表示跳过这整个数组后的地址,中间差了 9 个元素,并且 arr 地址比 &arr + 1 地址小,所以为 -9
//arr 和 &arr 的区别将会在指针数组中讲到
3.指针的关系运算:只有两个指向同一数组的指针才可以比较,比较的是地址的前后,若是两个无关的指针比较一般编译不通过
野指针:
野指针就是指针不知道指哪去了,指向随机地方,指向不可访问区域等指针
成因:1.指针未初始化 2. 指针越界访问 3.指针指向的空间被释放 等等
规避方法:
1.指针初始化,若有需要直接指向的地址则直接指向其初始化,若不知道指向哪则可以给指针赋值 NULL ,NULL 是C语言中定义的一个标识常量符,值为 0 ,该地址无法使用,避免误用出现野指针
2.小心越界访问:注意自己的程序向内存申请了哪些空间,避免指针访问超出空间
3.指针变量不再使用时,及时置NULL,并在再次使用前检查其有效性,这样可以避免指针的误用和错用
4.避免返回局部变量的地址,若要返回局部变量的地址可以使用 static 使变量暂时不被销毁
二级指针:
指向指针变量的指针,因为所指的类型为 int* ,所以二级指针的类型为 int* * (后面一个 * 表示这是一个指针,int* 表示这个指针所指的数据的类型为int* ),同理三级指针类型 int** *(三级指针都已经很少用)
其原理是指针也是变量也拥有自己的地址,不过它所存的数据也是地址,二级指针就是存的这个指针变量它自己的地址
特殊点的指针类型
接下来将会讲三个东西:数组指针,函数指针,空指针 ,其实这些指针类型没什么特殊的,只是比整型指针、字符指针等指针多一两个需要知道的知识点、然后类型看起来要复杂一点
数组指针:
声明:type (*name) [num],其中 type 为指针指向的数组的元素的类型,num为数组元素个数,指针类型为 type (*) [num]
要注意数组指针和指针数组的区别,数组指针是指向数组的指针,是指针,指针数组是元素为指针的数组,是数组。例:int *arr [10] 和 int (*arr) [10],若没有 () 则 arr 先和 [10] 组合,即数组,若有 () 则 arr 先和 * 组合,即指针
数组还有一个知识点必须知道,那就是数组名的在不同情况下的含义:
一般情况下数组名就是数组首元素的地址,但有两个例外:
1. sizeof(数组名),sizeof 中单独放数组名,这里的数组名表示整个数组,计算的是整个数组的大小
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址,地址值是首元素地址的值,但类型为这个数组的类型
像前面举的一个例子,arr + 1 和 &arr + 1 的不同的原因就是如此,arr 是首元素地址,指针类型为 int*,&arr 是整个数组的地址,指针类型为int(*)[10],但他们当中存的地址是一样的,不过类型不同导致解引用时效果不同
函数指针
声明 :type (*name) (type1, type2),其中 type 为指针指向的函数的返回类型,(type1,type2)为指针指向的函数的参数类型,指针类型为 type (*) (type1, type2)
要注意 函数名 和 &函数名 都表示函数的地址,两种写法的效果是一样的
空指针
声明: void* name
空指针可以接受任意类型的地址,但不能直接进行指针的 +- 整数和解引用的运算
空指针一般是当不确定会传过来什么类型的指针时,用来接收其地址的
到这里,本篇的指针讲解就结束了,其包含很多理性知识,大家一定要多多使用才能彻底将其消化理解