【C语言】指针概述(初阶)

一、内存与地址

在正式进入正文之前,我们先聊一个我们十分熟悉的事物:宿舍

我们刚来大学,是如何找到属于我们的宿舍的呢?几栋楼的哪个门牌号的宿舍。我们设想一下,如果我们没有宿舍的门牌号,我们要怎么才能找到我们的宿舍呢?只能一间一间宿舍进里面看,这样找效率就十分慢。

所以我们需要给宿舍贴上门牌号,能够提高效率,迅速找到对应的宿舍

对应到计算机中也同样如此。

内存就相当于与整个宿舍楼,内存有8G,16G等等,决定的是内存大小,对应就是宿舍楼大小。

在内存中,我们同样将内存划分为一个个内存单元(一个内存单元就是一个字节),并对每一个内存单元做了一个编号。每一个内存单元可以看成宿舍楼中的一间间宿舍,编号就是宿舍的门牌号。这样CPU就能快速找到对应的内存单元。

我们在二进制中了解到:1字节 = 8比特位,而这一个比特位可以看成宿舍中的一名学生(这个宿舍是8人寝宿舍)。

我们以32位的为例:

在计算机中,我们将内存编号称为地址。在C语言中,又将地址起了一个新的名字:指针。

所以:内存编号 == 地址 == 指针

二、指针与地址

那我们又该怎么理解指针这种数据类型呢?

在上面我们知道,指针 == 地址,联想一下int 类型的的变量,int类型的变量里面存放的是一个int类型的值,那么对于指针变量,里面也是存放了一个指针类型的值(也就是地址)。

我们要先了解,C语言创建变量本质是向内存申请了一块空间

由于a是int类型的变量,所以a的大小是4个字节。

地址分别是:00EFF9F8    00EFF9F9   00EFF9FA   00EFF9FB

那么我们如何得到a的地址呢?

这就是我们在操作符中了解到的&(取地址操作符)

此时,我们发现p中的值只有一个----a中四个地址中最小的地址。

结论:&取地址操作符是取出变量最小的地址。

那我们如何通过地址去访问地址中的内容呢?这就需要*(解引用操作符)

有人可能会想:我直接使用a变量就行,还更简便,为啥要取出a的地址,再用p去访问,麻烦。

这样做可以多出一条路径去使用变量a,使代码更加灵活,在之后多次使用中会理解。

三、指针变量类型意义

1. 拆解指针

我们了解到:指针变量中存放的是地址,但是地址怎么会有int ,char等数据类型呢?

确实,地址本身没有那些数据类型,所以对于int * p;* 表示p变量是一个指针,* 前面的数据类型表示的是p这个变量指向的对象的数据类型,例如:我们这个p变量就是指向一个int类型的对象。

同样,如果我们要得到一个char变量的地址,就创建这样的指针:char* pc;

2. 指针变量意义

接下来,我们具体谈谈这些指针变量类型的意义是什么:

我们先来看看这两个代码:

现象:第一个代码将a的四个字节全部改为了0,第二个代码只将a的第一个字节改为了0

结论:指针的类型决定了指针解引用时有多大的权限(也就是一次操作几个字节)。

3. void* 指针

指针变量类型中有一种特殊的指针类型:void*,可以理解为无具体类型的指针变量

这种指针变量可以接受任意类型的地址,是不是很好用,但是同时有很大缺陷:无法直接进行解引用和指针+-整数操作。(因为无法确定一次操作几个字节)(可以使用强制类型转换变为其他指针类型后再操作)

我们如果要使用int* p 接受一个char类型的地址,编译器就会发出警告:

但是使用void* p接受就不会:

那我们试试用void* 类型的指针进行解引用:

果真,编译器直接报错,无法执行程序。

同样,我们让void* 类型指针 +- 整数试试:

那么,void* 类型指针到底有啥用呢?

如果我们需要实现泛型编程,我们并不清楚会传来什么类型的地址。这时,我们可以让参数中的指针类型为void*,使得编写的函数能够实现对多种数据的处理。(在进阶指针中讲解)

四、指针运算

1. 指针 +- 整数

我们把指针看成日期,日期 +- 天数结果是什么,还是日期。指针 +- 整数 结果也是指针。

我们先看一个代码:

现象:int*类型的指针+1----跳过4个字节(int类型大小),char*类型指针+1----跳过1个字节(char类型大小) 

结论:type * 类型的指针,+i 后,跳过i * sizeof(type)个字节大小(相当于跳过 i 个type类型的数据)。

我们知道数组中的元素在内存中是连续存放的,我们可以使用指针来访问数组中每一个元素:

2. 指针 - 指针

首先,我们思考一下,日期 - 日期的结果是什么?这两个日期之间的天数。

那么指针 - 指针 也是同样的,表示的是这两个指针之间的元素个素(可以为负数)

我们先来看下面这个代码:

切记:指针 - 指针 不是两个指针地址的值得差值(也就是字节个数)。

由于这个原因,指针 - 指针有两点需要特别注意:

①这两个指针一定要是相同的类型,否则警告。

②这两个指针一定要指向相同的对象,否则结果无意义。

(因为两个变量的内存空间有空隙,结果与编译器有关)

3. 指针的关系运算

我们对于日期的比较,比较的是两个日期的值,指针的比较也同样如此,比较地址值得大小

五、野指针

野指针:指针指向的位置是不确定的(随机的,不可知的)。

例如:

我们可以将野指针比作野狗,当我们创建了一个野指针,相当于有一只野狗在街上走,谁也不知道这只野狗什么时候会暴起伤人,所以我们应当避免出现野指针。

在编程中,我们使用野指针p就是对一块不属于我们程序员自己的空间进行操作,一旦p指向的地址改变了,我们就无法找到那块空间,也就无法使用那块空间了。

1. 野指针成因

1️⃣指针未初始化

2️⃣指针越界访问

3️⃣指针指向的空间释放

2. 规避野指针

我们知道了野指针如何形成的,那么只需要对症下药就行。

1️⃣指针初始化

在我们创建指针时,如果不知道指针指向哪里,我们可以给指针赋值NULL。

NULL 是C语⾔中定义的⼀个标识符常量,值是0,0也是地址,这个地址是⽆法使⽤的,读写该地址 会报错。

2️⃣小心指针越界

作为程序员,我们应该清楚自己申请了哪些空间,指针也只能访问那些空间,超出范围的空间不进行访问。

3️⃣避免返回局部变量的地址

为了防止造成野指针第三种成因,不要返回局部变量的地址。

4️⃣指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性

我们使用指针指向一块地址,就可以通过指针访问这块地址。但是,当我们不在使用那个指针时,可以将该指针赋值为NULL,当指针指向NULL时,无法对该指针进行解引用等操作。同时,在使用指针之前,我们可以判断该指针是否为NULL。

六、const关键字

1. const修饰变量

我们知道,变量是可以被修改的,但是我如果要创建一个变量,这个变量不能被修改怎么办呢?

这时,就是const关键字出手的时候了。const修饰的变量在语法上不允许被修改,只能初始化。

我们说过,指针通过解引用就等价于指针指向的变量,那么我们能不能通过指针修改呢?

答案很明显:能。这跟我们的需求发生了矛盾,我们能不能让指针也无法修改地址中的内容呢?

2. const修饰指针

我们先来看看下面的代码:

当我们在指针前面也加上const修饰后,也无法通过p解引用来修改b的值了。

在语法上:const修饰指针可以放在两个位置:在* 前面,在* 后面。

①在* 前面:就像上面的代码一样,无法通过指针解引用修改地址中的内容,但是可以修改指针指向的对象地址。

②在* 后面:这个就与第一种相反,能够使用指针修改地址中的内容,但是不可以修改指针指向的对象地址。

所以,当我们需要一个指针既不能修改内容,也不能更改指向的对象,可以两个const一起用。

例如:const int* const p;

  • 39
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值