野指针、空指针、空悬指针
野指针
定义:只想一块未知区域(以及销毁或者访问受限的内存区域外的已存在或不存在的内存区域)的指针,被称作野指针。野指针是危险的。
危害:
① 引用野指针,相当于访问了非法的内存,常常会导致段错误(segmentation fault),也有可能编译运行不报错。
② 引用野指针,可能会破坏系统的关键数据,导致系统崩溃等严重后果
野指针产生的场景:
-
变量未初始化,通过指针访问该变量
int a; int *p = &a;//p是野指针 printf("%d\n",*p);//访问野指针,数据不安全
-
指针变量未初始化
int *p; // p 是野指针 printf("%d\n",*p); int a = get(); p = &a;
-
指针指向的内存空间被(free)回收了
int *p = malloc(4); *p = 12;//此时的p不是野指针 free(p); printf("%d\n",*p); // 此时的p就是野指针
-
指针函数中直接返回了局部变量的地址
int *get_num() { int a = 15; int *p = &a;//此时p对应的数据是一个局部作用域的数据 return p; } main() { int *p = get_num();//此时p是野指针 }
如何避免野指针?
-
指针变量要及时初始化,如果暂时没有对应的值,建议赋初值NULL。
-
数组操作(遍历和指针运算)时,注意数组的长度,避免越界
-
指针指向的内存空间被回收,建议给这个指针遍历赋值为NULL
int *p = (int *)malloc(10); free(p); p = NULL;
-
指针遍历使用之前要检查它的有效性(非空检验)
int *p = NULL; // if(p == NULL) if(!p) { return -1; }
空指针
很多情况下,我们不可避免的会遇到野指针,比如刚定义的指针无法立即为其分配一块恰当的内存,又或者指针指向的内存已经被释放了等等。一般的做法是将这些危险的野指针指向一块确定的内存,比如零地址内存(NULL)。
定义:空指针即保存了零地址的指针(赋值为NULL的指针),也就是指向零地址的指针。(NULL是空常量,它的值是0,这个NULL一般存放在内存0x0000 0000的位置,这个地址只能存NULL,不能被其他程序修改)
示例:
// 1.刚定义的指针,让其指向零地址以确保安全
char *p1 = NULL;
int *p2 = NULL;
// 2.被释放了内存的指针,让其指向零地址以确保安全
char *p3 = malloc(100);
free(p3);
p3 = NULL;
空悬指针
在C语言中,悬空指针指的是指向已删除(或释放)的内存位置的指针。如果一个指针指向的内存已经被释放,但指针本身并未重新指向其他有效的内存地址,那么这个指针就变成了悬空指针。悬空指针会引发不可预知的错误,并且如果一旦发生,就很难定位,因此在编程中尽量避免使用悬空指针。
// 2.被释放了内存的指针,让其指向零地址以确保安全
char *p3 = malloc(100);
free(p3);
printf("%p,%c\n",p3,*p3);//此时地址依然可以访问,但是地址对应的原本数据不可访问
void与void*的区别
定义:
-
void:是空类型,是数据类型的一种
-
void*:是指针类型,是指针类型的一种,可以匹配任意类型的指针,类似与通配符,又被叫做万能指针。
void:
-
说明:void作为返回值类型说明,表示没有返回值;作为形参,表示形参列表为空,在调用的时候不能给实参
-
举例:
//函数定义
void fun(void){..}//等效于 void fun(){..}
//函数调用
fun();
void*:
-
说明:
-
void*是一个指针类型,但该指针的数据类型不明确,无法通过解引用获取内存中的数据,因为
void*
不知道访问几个内存单元。 -
void*是一种数据类型可以作为函数返回值类型,也可以作为形参类型
-
void*类型的变量在使用之前必须强制类型转换,明确它能够访问几个字节的内存空间
int *p = (int*)malloc(4);
-
-
说明:
-
void*作为返回值类型,这个函数可以返回任意类型的指针
-
void*作为形参类型,这个函数在调用时,可以给任意类型的指针
-
-
总结:
-
void*类似于通配符,不能对
void*
类型的变量解引用(因为不明确数据类型,所以无法确定内存单元的大小) -
void*在间接访问(解引用)前要强制类型转换,但不能太随意,否则存和取的数据类型不一致
-