3.野指针
什么是野指针?
野指针就是 指针指向的位置是不可知的 (随机的、不正确的、没有明确限制的)
野指针是怎么形成的呢?
1.指针未初始化
int main() { int *p;//局部变量指针未初始化,默认为随机值 *p = 20; return 0; }
事实上,上面这个代码是无法运行的:
2.指针越界访问
int main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //当指针指向的范围超出数组arr的范围时,p就是野指针 *p = i; p++; } return 0; }
当循环到第十次时,*p已经指向arr[10],此时数组已经越界,即使编译也会出现问题:
3. 指针指向的空间释放
int* test() { int a = 10; return &a; } int main() { test(); int* p = test(); *p = 20; return 0; }
当main函数中执行到*p = &a时,变量a已经销毁,此时p访问的就是一块释放掉的地址,p就是一个野指针。不过,与1不同的是,这个程序可以运行。
如何规避野指针?
1. 指针初始化
当我们不知道指针p初始化为什么的时候,可以定义为空指针:
int* p = NULL;
而当我们知道p初始化为什么的时候,更要对他进行初始化:
int a = 10; int* p = &a;
2. 小心指针越界3. 指针指向空间释放及时置NULL4. 指针使用之前检查有效性如果我们合理初始化指针,当指针指向的内存被释放后及时置成空指针,那指针只有两种状态:有效指针和空指针,所以在调用指针时检查它的有效性就很有成效:int main() { int *p = NULL; //.... int a = 10; p = &a; if(p != NULL) { *p = 20; } return 0; }
4.指针的运算
指针+-正数
在指针类型的介绍中我们了解到,指针类型一方面决定了指针的访问权限,另一方面,指针类型还决定了指针加减一个整数时跨的长度。例如int*指针+1一次隔四个字节。
指针 - 指针
两个指针之间也可以进行减法运算。它的结果是两个指针间元素的个数。例如:
但是,要避免不同类型的两个指针相减。虽然编译器可能成功运行,但是两个地址之间元素的类型是什么呢?是整型还是短整型还是字符型?这样的话元素个数就没有意义了。
知道了指针的这种运算,我们又多了一种模拟实现strlen()函数的方法:
int my_strlen(char *ch) { char* start = ch; char* end = ch; while (*end != '\0') end++; return end - start; }
指针的关系运算
实际上,指针之间也可以进行比较:
在上面的代码中,p指向的是数组arr首元素的地址,pend则是指向的数组arr最后一个元素的地址,而数组的存储是从前向后存储的,所以可以理解为pend比p更靠后,更大。
值得注意的是,标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。