野指针与段错误问题
一、什么是野指针
所谓野指针,就是指针指向一个不确定的地址空间,或者虽然指向一个确定的地址空间,但引用空间的结果却是不可预知的,这样的指针就称为野指针。 案例1:
int main(void) {
int *p;
*p = 10;
return 0;
}
2
3
4
5
在本例中,p是自动局部变量,由于p没有被初始化,也没有被后续赋值,那么p中存放的是一个随机值,所以p指向的内存空间是不确定的。访问一个不确定的地址空间,结果显然是不可预知的。 案例2:
int main(void) {
int *p=0x13542354;
*p = 10;
return 0;
}
2
3
4
5
在本例中,p虽然指向了一个确定地址0x43542354的空间,但是它对应的空间是否存在,其读写权限是否满足程序的访问要求,都是未知数,所以导致的结果也是未知的。
通过以上两个例子可以了解到,在给指针变量绑定地址指向某个空间时,一定要是确定的,不能出现不可预知性。一旦出现未知性,它就是一个野指针,即使某一次没有产生严重后果,但埋下了这颗“地雷”后,就留下了不可预知的隐患,对于程序来说这是不可接受的。
二、野指针可能引发的危害
① 引发段错误 段错误就是地址错误,其实是一种对程序和系统的保护性措施。一旦产生段错误,程序会立即终止,防止错误循环叠加,产生雪崩式的错误。
② 未产生任何结果 有的时候,使用野指针虽然指向了一个未知的地址空间,但是这个空间可以使用,而且该空间和程序中的其他变量空间没有交集,对野指针指向的空间进行了读写访问后,也不会对程序产生任何影响。虽然如此,这种野指针也是必须要极力避免的,因为这个隐患很可能在后面的程序运行中导致严重的错误,而且这种错误很难排查。
③ 引发程序连环式错误 访问野指针产生的错误循环叠加,轻则程序结果严重扭曲,重则直接导致程序甚至系统崩溃。
三、野指针产生的原因
野指针产生的原因大概有如下三种。
❶ 在使用指针前,忘记了将指针变量初始化或者赋值为一个有效的空间地址,导致指向的不确定。
❷ 不清楚某些地址空间的访问权限,但是指针试图指向这些空间,并且按照不允许的权限去操作,如下所示。
int *p = "hello" *(p+1) = 'w';
由于hello作为字符串常量,存放在内存中的常量区中,该段内存只允许读操作。但是该例子却想将'e'修改'w',试图写不允许写的空间,一定会导致段错误。
❸ 访问空间时,内存越界导致野指针,如下所示。
int buf[4] = {0};
*(buf+4) = 10;
2
数组只有0、1、2、3这几个成员,但是例子中却试图访问超出数组范围的空间,导致要么段错误,要么程序结果不对,要么虽然没有明显错误,但是留下一个隐患可能会导致程序崩溃。
四、如何避免野指针
避免野指针的方法大致有如下四点。
❶ 养成良好习惯,定义指针变量时,将其赋值为NULL。如果说真的访问到NULL了,起码直接导致段错误,可以立即终止程序的运行,避免更加严重的错误,而且这种情况导致的段错误也好排查。
❷ 在使用指针变量前,一定要对指针变量初始化或赋值,让它指向一个有效且确定的空间。
❸ 检查指针的有效性,在使用指针前,做一下指针是否为空的判断,如下所示。
int *p=NULL;
if(NULL != p) {
*p = 100;
} else printf("p == NULL\n")
2
3
4
对于底层的驱动程序而言,在使用某个指针之前,先做指针是否为空的判断是很有必要的。因为绝大多数的野指针,都是由于我们忘记了初始化或者赋值导致的,这样的判断可以及时提醒我们有没有初始化或者赋值,通过这种方式可以在很大程度上避免野指针的情况。但是另一种情况就很难预防了,比如我们给指针变量初始化或者赋值时,自己给错地址了,导致指向的空间无效,面对这种情况时,程序员自己就需要多加小心了。
❹ 当我们确定某个指针变量不再使用时,我们就将其赋值为NULL,这么做的目的,就是防止它继续指向不再需要的空间,从而可能导致内存空间的值被篡改。
如果严格遵守以上规则,代码会写得很累。如果代码量少且程序员本身经验又很丰富,可以不用按照上面的步骤来做,但是如果本身经验不足且项目较大时,我们还是尽量按照上面的步骤来做。
五、NULL到底是什么
在C/C++中,NULL是这么定义的。
ifdef _cplusplus // 如果这个符号定义了,表示当前运行的是C++环境,否者就是C环境
#define NULL 0 // 在c++中NULL被定义为0
#else
#define NULL (void*)0 // 在C中NULL被定义为(void*)类型的0
#endif
2
3
4
5
NULL在C++和C语言中,会被定义成不同的形式。在C++中,NULL指针可以表示为整型数字0,不会做严格的类型检查;但是在C语言中就不行,会做严格的类型检查。
六、段错误产生的原因汇总
① 什么是段错误(Segmentation fault)
段错误本质上指的就是指针错误(地址错误),之所以称为段错误,大概是因为C语言的内存结构是由不同的内存段组成的,所以指针错误又被称为了段错误。
② 段错误产生的原因
段错误分为两种,一种是大段错误,另一种是小段错误。大段错误产生的原因是,指针变量指向的地址空间根本不存在。小段错误产生的原因是,指针变量指向的地址空间存在,但是对该空间的操作权限受到了限制,比如希望写,但是该空间不允许写,这也会导致段错误。