本文标签: 野指针 指针运算
前言
知道了指针的一些基础概念后,相信大家对指针有了一定的认知,不懂得可以参考我的上篇文章【初阶指针 1 ---从入门到入土】
下面我们继续
一、野指针
基本概念:野指针就是指针指向的位置是不可知的(随机的 ,不正确的 , 没有限制的)
就好比在街上有一只流浪狗, ,我们就叫它野狗. 野指针的意思也是一样,这个指针指向的空间是不属于我们的,是不可知的,那么他就是个野指针.(内存空间的使用需要我们去申请访问才能去使用它,不申请访问这块空间就不属于我们,无法访问)
而造成野指针的原因有三种情况:
1. 第一种.指针未初始化:
创建一个指针变量 p ,不初始化它的话,就会默认它是一个随机值.它所指向的空间不属于我们,给这个指针变量赋值就会造成非法访问. 这里就是个野指针.
如下代码:
可以看到编译器报错,未初始化变量 p ,造成野指针.
2.第二种.越界访问:
定义一个数组arr,将它初始化为全0;将数组名传给一个指针变量 p (数组名是首元素地址)
i 从0开始,进行 10 次 i++ ,当循环进行第 11 次时, p ++就超过了原来数组的大小 10 ,p 如果再向后访问内存就越界了.
将它解引用,就会再向后访问 4 个字节的空间,而这 4 个字节的空间并不属于原来的数组,就会造成非法访问内存,这个指针就是野指针.
如下代码:
可以看到程序崩溃,这就是越界访问导致的.
3.第三种.指针指向的空间释放:
假设向内存申请了一块空间,知道了它的地址 p ,如果将这块空间释放,这块空间就不再属于我们,但是指针 p 还存有原来空间的地址,而如果我们通过这个指针再次访问这块空间,这就会非法访问内存,造成野指针.
如下代码:
int *test()
{
int a = 10;
return &a;
}
int main()
{
int*p=test();
*p = 20;
return 0;
}
程序开始,调用 test 函数,进入 test 函数内部,创建了局部变量 a ,假设 a 的地址为 0x1122,函数返回的是 a 的地址 0x1122 ,回到主函数,这块地址放到指针变量 p 中,意味着现在 p 的地址就是 0x1122,此时 test 函数中的 a 的生命周期已经结束,已被释放.如果还想通过指针变量来找到他的地址就不可行了,这块空间已经不属于我们,会造成非法访问.
4.如何规避野指针:
1.指针初始化
2.小心指针越界
4.指针指向空间释放后及时置NULL,但是NULL不能使用(当不知道p应该初始化为什么地址时,直接初始化为NULL,就好比将上文的野狗拿绳子拴起来)
4.指针使用前检查有效性,(C语言本身不会检测指针越界)
二、指针运算
1.指针+-整数
代码如下(示例):
int arr[20]={0};
int *ptr=arr;
for(i=0;i<20;i++)
{
(*ptr)++;
ptr++;
}
上面的代码将 arr 数组地址给了指针变量 ptr (即首元素地址传给 ptr ),进入循环,循环进行 20 次,先将 ptr 的地址进行解引用,也就是将 arr 数组的首元素地址解引用再+1,再将 ptr +1,也就是地址+1,跳到下一个元素的地址,然后再进行解引用,依次下去改变了数组里每个元素的值.减法也是同样的道理.
2.指针-指针
代码如下(示例):
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d", &arr[9] - &arr[0]); //9
return 0;
}
arr[9] 取地址也就是第 10 个元素的地址减去arr[0] 第一个元素 1 的地址,我们可以发现,两个指针相减得到的结果是两个指针之间元素的个数.
但是我们要注意的是,指针和指针相减的前提是两个指针指向的是同一块空间.另外,如果是指针+指针是没有任何意义的.
3.指针的关系运算
举例:
define 定义一个值N_VALUES 为 5 ,然后定义values数组, 5 个元素,再定义一个指针变量vp.
在 for 循环里初始化 vp 为 values 数组首元素的地址, vp 的地址如果小于 values 数组的第 5 个元素的地址,循环就继续,进入循环vp解引用就将首元素赋值为 0 ,然后再++(因为是后置++, ++后就是下一个元素的地址),)再次进行判断,进入循环将 vp+1 解引用,第二个元素赋值为 0 ...以此类推,当 vp 指向下标为 4 的地址后再 ++ ,vp指向的地址就是下标为 5 空间地址(即valuse数组最后一个元素后面的那个内存位置),此时循环的判断部分不满足条件,循环停止. *vp++ 是指针+整数的运算, 而 vp<&values[N_VALUES] 就是指针的关系运算.
#define N_VALUES 5
float values[N_VALUES];
float* p;
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp ++= 0;
}
还是仿照上面的例子, vp 初始化为数组下标为 5 的地址(数组最后一个元素后面的内存位置),当 vp 地址大于数组第一个元素的地址时,继续循环,,循环过程中,第一次先将vp地址 -- 解引用后赋值为 0,也就是说将数组下标为 5 的地址 -- 后将下标为 4 的元素赋值为 0.,依次循环后,数组元素全都初始化为 0 .(如图)
for (vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
以上的代码又可以改为如下代码:
开始 vp 指向下标为 4 的元素地址,大于等于首元素地址循环进行,循环体中将下标为 4 的元素赋值为 0 ,vp 再 -- ,循环下去当 vp-- 指向首元素之前的内存位置时,循环停止,可能这样的代码让我们更容易理解.
但是C语言标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但是不允许与指向第一个元素之前的内存位置的指针进行比较.
for (vp = &values[N_VALUES-1]; vp >= &values[0]; vp-- )
{
*vp = 0;
}
总结:
--- 1.野指针就是指针指向的位置是不可知的,造成野指针会有三种情况.
--- 2.指针运算: 指针+-整数, 指针-指针,指针的关系运算.
感谢阅读.如有不足还望指出.
燕子,燕子你别走啊燕子,给个三连呗~~