再过几天就是霜降了,关于指针的知识大家有没有学会呢?
接下来,我会整理一下指针的相关的知识
不过,我希望各位观众,大家要明白一个事实:知识是可以学会的,指针属于知识,指针也是可以学会的!!!
1.内存和地址
1.1内存
1.1.1电脑中内存与电脑其他部分关联性:
1.1.2内存有着不同单位进行换算:
内存最小单位:比特(bit),一个比特位可以存放一个二进制位
1Byte = 8 Bit
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
…
内存单元:1个字节(Byte)
![在这里插入图片描述](https://ucc.alicdn.com/images/user-upload-01/128d9583ebca4d649cfd813dbb3730a9.
1.2编号的理解:
为了便于管理,我们给每个内存单元一个不同的编号。
编号的实现依赖于电脑硬件的约定俗称,而并不需要电脑进行存储。
如果有32根地址总线,那么就有2的32次方种不同的编号,因而就实现了内存地址的编号!!!
内存单元的地址 = = 单元编号 = = 指针
2.地址
2.1 取地址符
当我们想看某个变量的地址时候,我们可以写取地址符进行取地址操作
取地址符号:&
2.2指针变量和地址
当我们创建一个整形变量时候,会占用四个字节,当我们进行取地址操作时候,电脑会取地址哪一个呢?
答案是首地址,并且是内存中较低的那个地址
2.2.1指针变量
2.2.2指针变量的解引用
形式:* name
可以利用地址来修改指针指向的地址的内容
2.3指针变量的大小
同学们可以先猜一下指针变量的大小
下面直接代码来得出答案:
是不是没想到呢?怎么可能都是8啊!!!
答案简单,因为都是地址,地址都是由相同数量的2进制数字组成的,这个环境是64位二进制环境,转换成二进制数字就是64位,也就是64个bite位,也就是8个字节啊!
当然,如果平台是32位环境的话答案就是4字节,指针类型大小跟类型没关系,只跟平台环境有关,并且同一环境中类型大小是相同的!!!
3.指针类型的意义
有同学可能就想问了,明明指针大小都是一样的,区分指针类型有什么意义啊?
我们通过举两个方面的例子来探索其中的奥秘!
3.1指针的解引用中指针类型的意义
下面,我们通过一段代码,来了解一下指针不同类型的意义,注意观察对比!!!
注意:
1.那么,有些同学可能就想问了,char*p1可以存的下int num2 的地址吗?当然可以!因为地址的大小都是一样的
2.我们发现int类型的解引用修改了四个字节的内存,但是char类型的解引用修改了一个字节的内存!!!
结论:
1.指针类型有意义:我们发现,不同类型的指针类型是有意义的
2.访问权限的不同:同时,还可以证明不同类型的指针类型解引用的访问权限是不相同的,比如int类型的指针解引用操作可以访问4个字节,而char类型的指针解引用操作只可以访问1个字节
3.2指针中的±整数操作
我们发现,p1没有正常访问到数组数值,p2却正常访问到了数组的第二个值!
结论:
1.指针类型是有意义的
2.不同的指针类型步长是不同的
3.3数组访问
一般我们在访问数组时候,会通过下标进行访问,如下面的这种情况:
但是,我们也可以使用地址对数组进行访问:
那为什么数组可以这样地址加一访问呢?
原来,数组的地址由低到高依次排列,并且是连续的,这样,我们在首地址已经知道的情况下,就可以通过不断整型加一进行依次访问!!!
4.const修饰指针
const是用来限制指针的作用范围的,能够避免在不必要的地方使用。
比如:我们想要修改a的值,发现直接修改被限制了,但是我们可以通过指针修改啊!
所以:
4.1如果我们在指针的号前面加上const,限制的就是p:
4.2如果我们在指针的*号后面加上const,限制的就是p:
5.指针+,-整数的操作
5.1指针加减整数的操作
我们这里可以再讲一下关于数组的例子:
也可以写成这样哈:
5.2指针减指针的操作
在这里,指针减指针有个前提条件,就是使用时候必须得保证两个指针变量指向同一块空间,这样才能又意义,否则就因不确定中间的内存间隔而失去了意义!
5.3自主模拟实现strlen函数:
方法一:
方法二:
6.野指针
所谓的野指针,指的是容易越界访问,访问不确定,指针存在随即地址的指针。
6.1野指针的形成原因:
- 指针未初始化
- 数组访问超出范围
- 指针指向空间被释放
6.1.1指针未初始化
编译器报错:
6.1.2数组超出范围
如果指针越界访问,同样会造成野指针的情况
正常情况下:
如果越界访问:
6.1.3指针指向的空间被释放
这是因为:当解引用a时候,发现a所指向的地址被电脑释放了,因而所输出的数字是错误的。
6.2如何规避野指针呢?
野指针的发生像火灾一样,我们可以规范人们的行为减少火灾发生的可能性,但不可能绝对性的避免。下面可以给大家几条建议来提高指针的安全性:
- 指针初始化
- 小心指针越界
- 指针变量不再使用时,及时置为NULL
- 避免返回局部变量的地址
6.2.1指针初始化:
6.2.2小心指针越界
6.2.3指针变量不再使用时,及时置为NULL
正确的写法:
7.assert断言
用在运行时确保程序符合条件
必须要包含头文件:assert.h
assert的运行逻辑是如果判断为真,不会起任何作用,返回值非零;如果判断为假,终止报错,返回值为零。
assert的开关是#define NDEBUG
下面我们可以通过几行代码来体验一下:
8.指针的使用和传址调用
8.1传值调用与传址调用
两者的区别是什么?传值传的是一个数值,传址则直接把地址传过去。
为了更好的理解这两个词,我们用一个题目来做区分
题目:写一个函数,实现两个变量的数值调换
咱们先不做这个题目,先让大家判断一下下面的代码是不是传值调用:
我想大家应该猜到答案了,这不属于传值调用,因为main函数内部进行的交换。
那下面我们来正式的解决这个题目,有同学可能会写出下面的代码:
但是结果真的对吗?
好像我们发现没有交换,那到底问题出现在哪里呢?
原来,当我们创建一个自定义函数,自定义函数内的变量是当我们进入自定义函数时临时创建的,自定义函数内的变量的值是传入值的一份临时拷贝,当在自定义函数当中完成运算,自定义函数种的变量的值也会随之销毁,也就是说,实际上的num1和num2并没有发生交换!!!那么这种仅仅传入数值,并且新建一个变量拷贝一份数值的调用情况,我们可以称之为传值调用。显然,在本题目当中该方法并不适用。
那我们可以换一种方法:传址调用
看,两者成功调换了数字。
总结:
同过上面的案例,我们就可以得出以下结论,
1.当我们要修改函数外部参数时候,我们需要用到传址调用;
2.当我们不修改函数外部参数,仅仅在内部做一些比较排序打印等等的工作并不影响外部参数的数值时候,我们可以选择传值调用。
8.2strlen函数的模拟实现
首先,我们来回顾一下strlen函数的功能吧:
strlen函数功能:是用来计算字符串长度大小的库函数,这其中不包含字符串后尾的“\0”.
我们用代码来实现一下:
当然,我们依靠指针学到的知识也可以模拟实现一下:
好了,为了避免内容过多,我们暂时先说到这里吧,很快就会更新下一期指针内容哦~