在C语言中,指针是最常用也是最容易出错的地方,不规范使用指针很容易造成程序奔溃,在代码中应该谨慎使用指针,避免产生和使用野指针。所谓野指针,就是该指针是一个随机的地址值,这个随机值可以指向任何不确定的存储空间,因此会导致编译错误或是其他更严重的问题。
两种野指针
- 指针没有对应任何真实的存储空间 :这种情况会导致编译错误,编译器会产生告警信息帮助我们排查;
- 指针指向其他存储空间:如果指向空间不允许操作,编译器也会提示错误;但如果所指向的空间允许操作,则很可能会导致某个存储空间的数据被篡改,这种编译器不会报错,很难排查;
如何避免产生野指针?
定义指针变量必须要初始化,而且操作指针变量前最好先判断指针是否为空。
初始化指针
int a = 100;
int *p1 = &a; // 初始化为需要的值
int *p2 = NULL; // 当不确定初始化为何值时,初始为NULL
将指针初始化为 NULL 的好处在于:当忘了给指针变量复制一个合法指针时,已经被初始化为NULL的指针在编译时会提示错误,便于排查错误
使用前判空
int a = 100;
int *p = &a;
if(NULL != p) {
printf("p 中所存放的指针为:%p \n",p); // p -> &a
*p = 200; // 当p所指向空间不为空时,才能进行解引用
}
printf("p所指向的空间存放的值为:%d \n",*p); // *p 解引用
NULL的定义
NULL 的宏定义在 stddef.h 中,如下:
#if defined (__cplusplus)
#define NULL 0 //在C++ 中 NULL 代表 0
#else
#define NULL ((void*)0) //在C中 NULL 表示 (void *)0
#endif
段错误
在Linux系统中的指针错误,称为段错误(Segmentation fault)。程序中使用野指针就可能出现段错误,如没有初始化的全局指针变量或局部指针变量。
以下情况会出现段错误:
1.指针所指向的空间不存在
2.指针所指向的空间存在,但没有读写权限
什么是空指针?
空指针,即 void *,在使用空指针时,必须先强制转换数据类型,然后才可以正常使用。malloc函数返回的指针就是 void * 类型
void *malloc(size_t size);
// 因此在开辟动态内存空间时,需要对malloc返回的指针做强制转换,转换成需要的指针类型
int *p = (int *)malloc(sizeof(int));
void * 类型
通过 void * 用来兼容传递不同类型的指针,如下:
typedef struct student {
int number;
char name[10];
}Stu;
static int kind_tmp(int num, void *addr) {
switch(num) {
case 1:
*((int *)addr) = 100; // 将 addr 强制转换为 int *类型
break;
case 2:
*((double *)addr) = 3.1415;
break;
case 3:
((Stu *)addr) -> number = 3837; // 将 addr 强制转换为 Stu * 类型
strcpy(((Stu *)addr) -> name, "xiaoma");
break;
default:
printf("The point kind error");
}
return 0;
}
int main() {
int a = 0;
double b = 0;
Stu s = {0};
kind_tmp(1, (void *)&a); // 在传参之前将(int*)转换为(void*),方可传参
printf("变量将被赋值为 a = %d \n",a);
kind_tmp(2, (void *)&b);
printf("变量将被赋值为 a = %lf \n",b);
kind_tmp(3, (void *)&s);
printf("变量将被赋值为 number: %d \n",s.number);
printf("变量将被赋值为 name: %s \n",s.name);
return 0;
}
以上程序输出结果如下:
变量将被赋值为 a = 100
变量将被赋值为 a = 3.141500
变量将被赋值为 number: 3837
变量将被赋值为 name: xiaoma