我们知道在C语言中指针是很重要的知识,可以说指针就是C语言的灵魂,既然如此学好指针是非常重要的,下面就和大家分享指针有关知识。
一、指针的含义
1.内存和地址
在日常生活中,我们上学时所居住的寝室每个房间都有房间号或者门牌号。有了门牌号同学或老师要找到那你会非常的简单。
在计算机中上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数 据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何⾼效的管理呢?其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。
我们知道一个字节有8个比特位,所以每个内存单元就相当于一个学生宿舍,一个宿舍住了8个人。每个内存单元也都有⼀个编号(这个编号就相当 于宿舍房间的⻔牌号),有了这个内存单元的编 号,CPU就可以快速找到⼀个内存空间。
所以我们可以理解为: 内存单元的编号 == 地址 == 指针
2.编址
⾸先,必须理解,计算机内是有很多的硬件单元,⽽硬件单元是要互相协同⼯作的。所谓的协 同,⾄少相互之间要能够进⾏数据传递。但是硬件与硬件之间是互相独⽴的,那么如何通 信呢?答案很简单,⽤"线"连起来。 ⽽CPU和内存之间也是有⼤量的数据交互的,所 以,两者必须也⽤线连起来。 不过,我们今天关⼼⼀组线,叫做地址总线。我们可以简单理解,32位机器有32根地址总线, 每根线只有两态,表⽰0,1【电脉冲有⽆】,那么 ⼀根线,就能表⽰2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含 义,每⼀种含义都代表⼀个地址。 地址信息被下达给内存,在内存上,就可以找到 该地址对应的数据,将数据在通过数据总线传⼊ CPU内寄存器。
二、指针变量和地址
1.取地址操作符(&)
在之前学习的scanf库函数时我们都要使用&符号,这个符号就是取地址操作符。
如果我们创建了一个整型变量a,我们想要得到变量a的地址就要用&a
如:
这里我们得到a的地址就是00FFFC3C,这里要注意的是打印地址要用%p。当然我们知道一个整型变量占4个字节,&a取出的是a所占4个字节中地址较⼩的字节的地址,虽然整型变量占⽤4个字节,我们只要知道了第⼀个字节地址,顺藤摸⽠访问到4个字节的数据也是可⾏的。
2.指针变量
我们既然得到了一个地址就要想办法把它存储起来方便后面使用,这个时候就需要指针变量。
如:
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
上面我们看到pa的类型是int *,那么该怎样理解呢?其实很简单,* 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型int类型的对象。
当我们知道了这之后我们就可以举一反三:
了解了这些基本的指针变量后,接下来我们要学习怎么去用它们。
3.解引用操作符
当我们拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
如:
*pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a变量了;所以*pa = 0,这个操作符是把a改成了0。
我们可以这样理解上面*pa就等同于a。
指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。
⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。
4.指针变量的大小
我之前所学的知识告诉我们变量也是有大小的,如: int 类型的大小是4个字节, char 类型的大小是1个字节
那么指针变量的大小又是多少呢?我们不妨打印出来看一下
我们惊奇的发现它们的大小都是一样的!那是为什么呢?
前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。 如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。 同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的⼤⼩就是8个字节。
所以:
32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
三、指针变量类型的意义
1.指针加减整数
我们先来看两段代码
我们可以看出,指针加减整数,增加(减少)的是内存空间以字节为单位, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化。
所以:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
(图中地址是随便打的)
2.void*指针
在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指 针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进⾏指针的加减整数和解引⽤的运算。
如:
四、指针运算
指针运算分为三种:
指针加减整数
指针减指针
指针的关系运算
1.指针加减整数
上面说过指针加减整数跳过几个字节取决于指针变量的类型,那么我们可以来看这个例子,我们知道数组在内存中是连续存放的,所以只要我们知道数组首元素的地址就可以通过指针找到后面所有的元素
如:
这里的*(parr+i)就是指针加减整数,加i就跳过i*4个字节。
2.指针减指针
我们先看代码:
我们可以看到指针减指针得到的是指针之间的元素个数,准确来说指针减指针的绝对值到的是指针之间的元素个数,但是指针减指针的前提条件是:两个指针指向同一块空间。
那么指针减指针有什么用呢?
我们知道strlen函数是用来求字符串长度的函数,那么我么就可以用指针减指针来模拟实现strlen函数。
3.指针关系运算
还是先看代码:
从这里我们可以看到指针也是可以比较大小的,高地址要大于低地址。
五、野指针
1.野指针概念
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
2.野指针成因
(1)指针未初始化
如:
(2)指针越界访问
如:(3)指针指向的空间释放
如:
3.规避野指针
野指针的危害极大,一旦出现野指针,程序一般都会出错,那么我们该如何规避野指针呢?
(1)指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL, NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址会报错。
(2)⼩⼼指针越界
(3)指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性
(4)避免返回局部变量的地址
好了,以上就是指针的基础知识了,感谢大家观看!