文章目录
- 前言
- 一、指针变量类型的意义
- 指针的解引用
- 指针+-整数
- void*指针
- 二、const修饰指针
- const修饰变量
- 三、指针运算
- 指针+-整数
- 指针-指针
- 指针比较大小
- 四、野指针
- 野指针成因
- 如何规避野指针
- 总结
前言
本节课开始上点有意思的内容了!
一、指针变量类型的意义
指针的解引用
对比下面2段代码,我们在调试时观察内存的变化
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0
结论:指针的类型确定了,对指针解引用的时候就会有多大的权限(即一次能够操作几个字节)
比如 char* 的指针就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节,所以,就算每个指针变量的大小都是4/8个字节,但是我们仍然要注意类型
存在即合理,C语言既然给变量也设置的类型,就一定有它的理由
指针±整数
先看一段代码,我们调试来观察地址的变化
代码运行结果如下:
我们可以看出,char类型的指针变量+1跳过1个字节,int类型的指针变量+1超过了4个字节,这就是指针变量的类型带来的变化
结论:指针的类型决定了指针向前或者向后走一步有多大(距离)
void*指针
在指针类型中有一种特殊的类型是 void* 类型的,可以理解为无具体类型的指针(或者叫做泛型指针),这种类型的指针可以用来接受任意类型地址,但是也有局限性, void* 类型的指针不能直接进行指针的±整数和解引用的运算(因为不知道具体可以操作的字节数)
来个例子:
上述代码中,将一个int类型的变量的地址赋值给一个char类型的指针变量,编译器给出了一个警告,是因为类型不兼容,而使用void类型就不会有这样的问题
那么 void* 类型的指针有什么用呢?
一般 void* 类型的指针是使用在函数参数的部分,用来接受不同类型数据的地址,这样的设计可以实现泛型编程的效果,使得一个函数来处理多种类型的数据,这点,我们后面会单独介绍,相信我,这会让你感慨真酷!
二、const修饰指针
const修饰变量
变量是可以修改的,如果把变量的地址交给一个指针变量,通过指针变量的也可以修改这个变量
但是如果我们希望一个变量加上一些限制,不能被修改,这该怎么做呢?答案是采用const修饰符
上述代码中,n是不能被修改的,其实n本质是变量,只不过被const修饰后,在语法上加上了限制,只要我们在代码中对n进行修改,就不符合语法规则,就报错,致使我们没法直接修改n
但是我们绕过n,使用n的地址,去修改n就能做到了,虽然这样做是在打破语法规则
const修饰指针的时候
const可以放在的左边
const可以放在的右边
也就是说,const int* pa 和 int* const pa都是可以的
但是它们限制的不太一样,前者限制的是*pa,后者限制的是pa
如图:
哦对了,插一个小知识,这一定要搞清楚
1.p指向a,p里面存放的是a的地址
2.p是变量,有自己的地址
3.*p是p指向的空间
我们再回来研究这个const修饰
1.第一个const修饰 *p ,意思是不能通过 p 来修改 p 指向的空间的内容,但是 p 是不受限制的
2.第二个const修饰 p ,意思是 p 变量不能被修改了,没办法再指向其他变量了,但是 *p 不受限制,还是可以通过p来修改p所指向的对象的内容
3.当然还有 * 左右都限制,这时候 p 和 *p 都被限制了
三、指针运算
指针±整数
因为数组在内存中是连续存放的,只要知道第一个元素的地址,顺藤摸瓜就能找到后面的元素
指针-指针
结论:指针-指针的绝对值是指针和指针之间的元素个数,前提是两个指针指向同一块空间
指针比较大小
先插个小知识,arr数组名其实是数组首元素的地址,arr就等价于arr == &arr[0];
四、野指针
概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针成因
1.指针未初始化
2.指针越界访问
3.指针指向的空间释放
地址确实返回来了,可是从函数出来,a的内存空间就还给操作系统了,这时候你还找出这个空间,甚至还想访问它
如何规避野指针
1.指针记得要初始化,指向谁要清楚,如果一开始不知道要指向谁,就指向空指针NULL
NULL是C语言中定义的一个标识符常量,值是0,0也是地址,这个地址是无法使用的,读写该地址会报错
2.小心指针越界
要明白指针只能访问哪些空间,不能超出范围访问
3.指针变量不再使用的时候,及时置NULL,指针使用之前检查有效性
当指针变量指向一块区域的时候,我们可以通过指针访问该区域,后期不再使用这个指针访问空间的时候,我们可以把该指针置为NULL
把野指针想象成一条野狗,野狗放任不管是非常危险的,所以我们可以找一棵树把野狗栓起来,就相对安全了,>在使用前,我们就要判断一下是否为NULL
4.避免返回局部变量的地址
总结
本章开始已经有点意思起来了,但是难的还在后面!