在c语言的学习中,指针永远都是无法绕过的一点。下面就开始对c语言的指针进行讲解。
一、指针的概念
首先我们得知道指针是什么?
下面是前置的知识
总所周知,我们电脑的内存最小的单位是比特(bit),比他大一点的是字节(byte), 1 byte = 8 bit ,再之后还有Mb,Gb等内存单位,就不再赘述了。
电脑的内存有它自己的编号,这串编号就是地址,这样就可以方便进行数据的存储和拿出。编号是由0和1组成的,我们现在的电脑有32位和64位,其实就是电脑有着32根或64根地址总线。每根线的通电与否都可以表示数字 1 或 0 。所以内存的每个字节都会有它的编号,由 32 或 64 个 0 或 1 组成。至于不是每个bit都有编号的原因是bit实在太小了,所以不会用bit来作为基本单位,而字节作为基本单位刚刚好。
指针其实就像是光标,指向着某个字节,其实也就是指针变量存放了一个地址的信息(这里就用32位电脑举例)地址如果用二进制展示就太长了,一般显示出来是以十六进制显示出来的。
就像这里,指针p(也就是指针变量p)指向了地址值为0x00000001的空间(这里的由1开始的地址只是为了方便讲解),它也可以指向其他地址,这就要看我们自己的了。我们电脑的空间就是连续一长条,有的在使用,有的是空闲的。平时我们使用的空间一般是电脑自动分配的,电脑会把空闲的空间给我们使用,我们就可以用指针来指向这个空间。
二、使用指针
1.拿到地址
那么我们怎么知道电脑分配给我们的空间是哪一个呢?
我们就要用到我们的&符号,它叫取地址符,顾名思义,就是可以取出一个变量的地址。
这里就是用&取出了a的地址,然后用用%p形式打印出来(%p就是以地址形式打印),这里的地址就是 0095FAEC(十六进制)。
如何创建指针?
先写出指针要指向的地址的类型,然后加个*号,再为这个指针变量取个名字就行了(每个指针变量前都有一个*号)。
我们用创建了一个int类型的指针p,用它来存放了a的地址,这里分别打印p和&a,结果是一样的。(为什么地址与上次不同是因为电脑分配的空间是随机的空闲空间)
2.使用地址
如何使用我们的地址呢?
就要用到我们的解引用操作符,也就是*号,*号在定义指针变量时的意思与使用地址时的意思是不同的。
这里我们就用*号操作了指针p,然后输出。解引用的意思就是顺着地址找到那个对象。指针p指向的是a变量,这里的*p就找到了a,所以打印出来的就是a的值。
三、指针类型的意义
指针都是指向某个字节,那么区分指向不同类型数据地址的指针有什么意义呢,为什么不一个指针就存放任何地址呢?
各个类型占据的字节数是多少呢?
char 1 byte
short 2 byte
int 4 byte
float 4 byte
double 8 byte
所有的指针类型都是一个大小的,因为都是存放地址的,在32位机器里,地址有32个bit位,所以需要4个字节,而64位机器里有64位bit所以就要8个字节。
1.区分解引用时的读取范围
不同类型占据着不同的内存空间,我们拿到了这个类型占据的空间中第一个字节的地址,如果不给指针类型区分类型,那么我们顺着这个地址找对象的时候,电脑怎么知道是要读取 1 个字节的数据还是 4 个字节呢?
2.进行指针加减数字操作时会有区别
在对一个指针进行加或减数字操作时,指针指向的地址会跳过数字那么多个的数据类型的空间。
什么意思呢?
这里,p1是char类型的指针, char* 类型,p1 + 1 的地址就比&a1高了 1 (因为char类型是占 1 个字节),而p2是 int* 类型,p2 + 1 ,就比&a2 高了 4 。
3.指针减指针也会有区别
指针减指针得到的结果是两个指针中间隔的这个指针指向的类型那么大的空间有几个。
这里,就是 p2 - p1 的结果为 9 的原因, 二者都是int类型,所以它们的地址相减,中间的那么多字节全部分成一个个int类型,中间有 9 个那么大的空间,结果就是9。
所以给指针分类型显然是有区别的。
四、const修饰的指针
const是可以给一个变量附上常属性的,虽然它本质上还是变量,但给变量加上const就像加了一把锁,可以让它无法被改变
const修饰指针,可以放在三个位置
const int *p; 1
int const*p; 2
int *const p; 3
其中,第一种和第二种的效果是一样的。
现在来展示一下:
1.第一种
这里,我们想通过p指针来改变a的值,程序报错,而改变p指向的地址,没有问题。
2.第二种
结果和第一种一样
3.第三种
这里完全反过来了,通过p改变a的值被允许了,而想改变p指向的地址却不被允许
这里总结一下,分为两种情况,当const在*号的前面时,*p = 的行为被制止,相当于把*p给固定了,const在*号后面时,p = 的行为被制止,相当于把p给固定了。其实还可在*号的前后都加,这样,就都被固定了。
五、野指针
在使用指针是要注意的一点是,我们通过指针调用更改的必须是程序中已知的内容,不然就有可能报错,当一个指针指向了未知的空间,这个指针我们就称为野指针。
向这样子,p没有被我们赋值,里面的内容是未知的,我们对它进行访问,就会报错,还有数组的越界等等,使用了未被定义的地址,这些都是野指针。
六、特别的指针类型
指针类型分为好多种,由我们常见的 int*,char*, double* 等,更特别一点的就是函数指针,数组指针,空指针和二级指针等。
1.空指针
在赋值是,有时我们在没想到要给一个指针变量赋值什么的时候,就可以给它赋值NULL,它代表的就是空。
2.数组指针
这种类型的指针指向的是一个数组,对数组指针进行+1 操作,跳过的就是一个数组那么大的空间。
这个变量p,它是指针类型,指向的是大小为10个元素的整型数组,这里+1 就跳过了40个字节(即4*10)。
3.函数指针
这里我们来看一看函数的指针是怎么回事
首先,函数的 函数名 和 &函数名 ,的结果是一样的,它们都是函数的地址
这里,用简单的加法函数举例
函数指针的创建和数组指针有点相似,不过后面的 [ ] 换成了(),里面的数字换成了函数的参数类型,至于类型后面有没有变量名并不重要(也就是这里的a,b)。通过函数指针也很简单,就是把指针当成函数名,后面接(),内部填参数就行了。就和函数名一个道理,add和&add一样,pf 和 *pf 也一样。
4.二级指针
就像指针是存放普通变量的地址的,二级指针就是存放指针的地址的,相应的也就有三级四级等等,不过一般用不到。
二级指针其实就是和普通指针一样的,&是取一次地址,* 是解一次地址。
最后,别忘了点赞收藏加关注!!!😁