本节接上节内容主要讲述指针使用存在的问题————野指针以及如何规避野指针
一、野指针
所谓野指针,就是指针指向的位置是不可知的,野指针在代码中很危险!!!要尽量避免形成野指针
1、野指针形成的愿因
(1)、指针未初始化
如图所示,指针变量未初始化,会默认为随机值,编译器出现报错
(2)、指针越界访问
如图所示,随着p的不断增大即指针指向的范围超出数组arr的范围时,p就变成了野指针
(3)、指针指向的空间被释放
我们来看这段代码,这段代码看似能够正常运行,但实则存在问题,此时的指针p已经是野指针。
首先,在函数内部申请了一块空间创建了变量n,并赋值200,然后返回n的地址,用整型指针p来接收返回的这块地址。但要注意的是,当返回地址的这一瞬间,函数内部的栈帧空间被销毁,将这块空间返还给了操作系统,此时通过地址我们确实能找到n的值,但却不能访问这块空间了,此时p成为了野指针
(4)、动态内存分配中指针被free以后未置为NULL
若动态内存使用完后没有将p置为空指针,内存被再次分配,后续再次通过p指针读写数据时会造成很大的影响,一定要注意。
2、如何规避野指针
(1)、指针初始化
如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL
NULL是C语言中定义的一个标识符常量,值是0,0也是地址,但无法使用,读写该地址会报错
(2)、防止指针越界
通过指针向内存中申请了哪些空间,通过该指针也只能访问这些空间,一定不要超出范围访问
假如指针变量超出范围,如左图,最后p指向的地址已经超过了范围,这时要及时置为NULL,防止形成野指针
(3)、指针变量使用完后,及时置为NULL,同时在使用之前检查指针有效性
指针使用之前可以判断是否为NULL或者assert断言检查,如果该指针是空指针就不去使用,使用完后赋值为NULL,如上面提到的动态内存分配中使用完后要及时置为NULL
(4)、避免返回局部变量的地址
如上面原因(3)的例子一样,不要返回局部变量的地址。
二、assert断言
assert()宏是在assert.h头文件下的,用于在运行时确保程序符合指定条件,若不符合就报错终止运行
我们可以用来检查指针是否为非空指针,这只是其应用的一个方面
assert()宏接受一个表达式 如果表达式为真assert()没有任何作用,程序继续运行
如果表达式为假 就会报错 提示没有通过的表达式以及出问题的文件和行号 极大地方便了我们见检查程序,但同时其也会增加程序运行时间
同时assert()断言机制还可以灵活地开启和关闭:若确认程序没有问题,则可以在assert头文件的前面定义一个宏NDEBUG 即#define NDEBUG 程序会自动禁用文件中所有的assert()语句