这里写目录标题
一.指针的概念
我们知道C语言中的变量和常量等都是存放在一定的空间里的,那么每一块空间也应该有一份属于自己的地址,这个地址就像是指向了这个空间,通过这个地址我们可以间接地访问到这个空间里的内容,所以我们形象把地址称作为指针。
比如说我们创建了一个变量a,那么a就存放在了一块空间中,如果我们知道a的地址,我们就可以通过这个地址来间接地去访问a,并且可以去修改a变量的内容。
★在计算机内存中,每一个字节的空间都有其对应的编号,这个编号就是上面说的地址,指针变量存放的是变量首字节的地址。
🚀二.指针变量的创建
1.创建的方式
在创建指针变量之前,我们会想到一个问题,就是指针指向的对象有多种,可能是整型,可能是浮点数,也可能是字符类型等等,并且我们知道这些类型所占的空间大小可能是不同的,所以我们应该也会有不同的指针变量类型来指向某一特定类型的变量,来说明指针所指向的变量类型是什么。
例如:
int*是整型指针变量,存放int类型变量的地址
char*是字符类型指针变量,存放char类型变量的地址
以此类推其他的指针变量
那怎么创建指针变量呢?上代码!
int a = 9;
int *pa = &a;
在上述代码中,我们就创建了一个int*类型的指针变量pa来存放a的地址(&就是取地址符号,返回变量a的地址)。
2.创建指针变量的注意事项
2.1野指针问题
我们在创建一个指针变量的时候,如果未进行初始化,那么这个指针变量的值就是一个随机值(它是随便指向一块空间的!不管这个空间有没有被使用),那么就有可能导致我们以后去间接访问这个地址内容的时候出错,因为访问的这块空间可能压根就没有东西嘛!这种乱指向一块空间或者说是指向的空间不合法的指针就如同一条没有拴绳的哈士奇,我们形象地称它为野指针。
那如何来防止出现野指针呢?
给它栓上绳就OK了。
int *p = NULL
在我们一开始不知道要给p赋什么值时,我们可以先把NULL(空指针)赋给p,在我们后面访问p指向地址的内容之前,可以先判断一下p是否为空指针,如果不是空指针,再继续后面的访问操作,这样做就能有效地规避野指针问题。
2.2同时创建多个指针变量
先上一段代码,这里我想同时创建两个指针变量
int* a=NULL,b=NULL;
大家认为这样有没有问题?
当然有的啊(要不然我怎么会提出来🤭)。
在我们创建变量的时候,其实*是和后面的变量名在一起的,也就是说在上述代码中,*已经和a跑了,所以a是一个指针变量是毋庸置疑的,但是后面的b就没*了呀,那么后面的b就相当于是int b = NULL,所以说这里创建的b变量是一个整型而非指针类型!
所以,如果非要同时创建多个指针变量,一定要写成下面这样。
int *a=NULL,*b=NULL;
当然,我觉得还是最好不要同时创建多个指针变量,易错。
🌳三.解引用
我们通过一个地址间接地去访问该地址的内容,这个操作就叫做解引用操作,解引用操作符是*
既然这样可以访问到这个地址里的真实内容,那我们就可以用指针解引用操作来通过一个函数改变其实参的内容了。
int Exchange(int *pa,int *pb)//函数接受两个数的地址
{
int tmp = *pa;
*pa = *pb;
*pb=tmp;//通过指针解引用改变了实参的值
}
既然我改变你的形参影响不了你的实参,那我通过你传过来的地址去改变这个地址里的内容总能改变得了吧!这就是解引用操作,非常好用。
🏰四.指针类型的作用
为什么我们要说明指针指向内容的类型,即为什么要说明指针类型呢?
1.访问空间大小权限
在一开始我们说过,计算机内存中的每一个字节都有其对应的地址,C语言中又有大小不同的各种变量,那我们解引用操作的时候到底是访问几个地址的内容呢?这时,C语言就会通过你给定的指针变量类型来对应地去访问对应的空间大小。
举个栗子:
如果我们创建了一个整型指针变量pa来存放整型变量a的地址,这是pa的值是a的第一个字节所在空间的地址,在之后对pa进行解引用操作时,编译器看到这是一个整型指针变量,就会去访问从a所在空间的第一个字节开始一直连续访问4个字节,这四个字节里的内容也就是a的值。
2.指针加减整数
指针类型也决定了指针加减整数所跳过的字节数量,如上述代码,int类型的指针加1跳过四个字节(一个整型变量的大小),而char类型的指针跳过一个字节(一个字符类型的大小)。
3.指针减指针
我们已经知道了在上述代码中,p1+1即为arr1数组的第二个元素的地址,我们假设其为p3,那既然p1+1=p3,那p3-p1不就等于1了嘛,所以,指针减去指针其实也就是等于两个指针跨过了多少个变量
🌳
1.指针变量类型决定了解引用操作时访问空间大小的权限(能访问多少个字符)。
2.指针变量类型决定了指针加减整数跨过的长度。
📕六.二级指针(初步认识)
C语言中的变量都有其对应的空间地址,指针变量也不例外,指针变量的地址就叫做二级指针。
int a = 10;
int *pa=&a;
int **ppa=&pa;
在上述代码中,ppa就是二级指针,它存放了指针变量pa的地址。通过解引用二级指针,我们可以访问到pa的内容,这就类似于套娃。二级指针在数据结构中是非常重要的。
七.数组指针
类似整型指针,字符指针,数组指针也是一个地址,它指向一整个数组。
int arr[3]={1,2,3};
int (*parr)[3]=&arr;
parr就是一个数组指针。*parr说明这是一个指针,剩下的int [3]则说明了这个指针指向的内容是一个含有3个int类型变量的数组,数组指针加1则跳过整个数组。
八.指针数组
和上面的数组指针不同,指针数组是一个数组,里面的元素类型是指针。
int*arr[3]={&a,&b,&c};
在这里定义的时候,由于[ ]的优先级高于*,所以arr先和[3]结合为arr[3],这不就是数组的定义吗,说明了数组中含有3个元素个数,然后前面的int*就成为了数组元素的类型,所以这就是一个数组,里面的元素类型是int*指针变量。
🐒总结:
1.创建指针变量的时候别忘记了赋值,预防野指针问题。
2.创建指针变量时,对于int*pa=&a来说*是和pa在一起的,但是我们通常也会说这个指针变量的类型是int*,要注意一下这个细节,这点也提示了我们在创建变量时,只要看到了一个变量名和*在一起,我们就可以知道这个变量是一个指针,指针所指向对象的类型就是除去*变量名之后剩下的东西。
3.指针解引用操作时要注意别越界访问,也就是别访问没申请的空间。
4.指针的类型决定了指针解引用操作时访问空间的权限大小,也决定了指针±整数跨过的距离。