一. 野指针
概念:野指针就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的)
1.野指针的成因&出现的情形
指针未初始化
前情提要:指针变量本意是通过存放特定的地址,解引用后,找到这个特定地址所指向的内存单元,并对其间接访问或改变。
具体情形(结合代码理解):
代码原目的:在整形指针变量follower中存入整型变量beauty的地址;在此基础上,通过整形指针变量follower解引用,来间接访问整型变量beauty,为其赋值520,从而最终间接将beauty中的0改为520。
//err
//follower指针未初始化:
#include <stdio.h>
int main()
{
int beauty = 0;
int* follower;//未初始化,默认为随机值,
//即存的是随机的地址,寻得的也是随机的内存单元
//非法访问内存,是错误的
//指针没有指向性,有违指针变量本意,
//所以是错误的,follower是一种野指针,
//不可能达到原目的
*follower = 520;
printf("%d\n", *follower);
printf("%d\n", beauty);
return 0;
}
//程序运行的结果:报错:“使用了未初始化的局部变量follower”;
//correct
int main()
{
int beauty = 0;
//下两步实则是将整型变量beauty的地址存入整型指针变量follower中,
//引入整型指针变量love_permission只是为了好做比喻,好解释的更清楚;
int* love_permission = &beauty;
int* follower = love_permission;
*follower = 520;
printf("%d\n", *follower);
printf("%d\n", beauty);
return 0;
}
//程序运行的结果:
//520
//520
奇妙比喻:
1.前情提要:
整型变量beauty——暗恋的女孩子
整型指针变量love_permission(=&beauty)——暗恋女孩子给的恋爱许可(e.g.给了你暧昧的机会,暗示你表白就可以在一起 等等)
整型指针变量follower——一个追求者的角色
2.生成故事:
err版:
follower这个追求者很怂,在表白墙上表白说520(*follower=520),但没有说是对谁表白(follower未初始化)。ta的表白没有特定的对象,不符合表白墙的规定,所以没有被刊登(程序运行报错)。
correct版:
吸取了上次的教训,懂得了“追女孩子犹豫就会白给”的道理后,这次follower超勇的,主动靠近自己暗恋的女孩子beauty,然后beauty对follower日久生情,给了ta恋爱许可(int* love_permission = &beauty)。
follower看懂了暗示,就向beauty表白了说520(*follower = 520),beauty接受了ta的告白也说520(此时由打印可知,变量beauty中的数据也变为520)。
3.恋爱教训:追女孩子要有指向性(指针要初始化),确认心意后再表白才能终成眷属(经初始化的指针,对其解引用才有意义)。
指针越界访问
具体情形(结合代码理解):
#include <stdio.h>
int main()
{
int girl[10] = { 0 };
int* over_male = girl;
int i = 0;
for (i = 0; i <= 11; i++)
{
//当指针指向的范围超出数组girl的范围时,指针越界访问,程序无法正常运行
//此时的over_male就是野指针
*(over_male++) = i;//不断间接访问girl数组,并对其赋值;
}
return 0;
}
奇妙比喻:
1.前情提要:
整型数组girl[10]——一个没什么主见(元素均初始化为0),但有原则(数组元素为10,即可以容忍的就是10项,再多就不接受了)的女孩子
整型指针over_male——一个大男子主义过头的男人
2.生成故事:
有一个没什么主见、但有原则的女孩,她给自己定了个标准:可以有10件事完全由男朋友做主,但超过10件,就觉得触碰了原则,就会分手(int girl[10] = { 0 })。
后来,她和一个男人over_male恋爱了(int* over_male = girl),这个男的非常的大男子主义,不断将自己的观点强加在她身上(for循环的赋值过程*(over_male++) = i,这个i很有灵性哈哈)。
终于,超过了10次,女孩的忍耐到了极限(i=10时,是第11次赋值了,数组越界访问了),就和他分手了(程序无法正常运行),而这个男人因为失去而痛彻心扉,但已经晚了(此时的over_male沦为野指针)。
3.恋爱教训:恋爱中不要太过大男子主义,要把握好度(指针不能越界访问)。
指针指向的空间释放
具体情形(结合代码理解):
int* test_love()
{
int playgirl_promise = 414;
return &playgirl_promise;
}//该函数调用结束后,playgirl_promise这一局部变量会销毁,
//因此返回去的是一个无效地址(指针);
int main()
{
int* innocent_boy = test_love();//因为playgirl_promise这一局部变量已销毁,
//所以返回来的地址指向的空间已释放,
//innocent_boy沦为无意义的野指针;
*innocent_boy = 5201314;//不能间接改变playgirl_promise中的值;
return 0;
}
//指针指向的空间释放——及时置空指针
int* test_love()
{
int playgirl_promise = 414;
return &playgirl_promise;
}
int main()
{
int* promise = test_love();
promise = NULL;//因为返回来的地址指向的空间已释放,所以置为空指针
int* innocent_boy = promise;
if (promise != NULL)
{
*innocent_boy = 5201314;
}
return 0;
}
奇妙比喻:
1.前情提要:
自定义函数int* test_love(void)——女孩playgirl发起的恋爱邀请,人如其名,是抱着一种试一试的心态,随便玩玩而已
整型变量playgirl_promise——女孩playgirl在恋爱时的承诺(414——试一试);
整形指针变量innocent_boy——把女孩的喜欢当真的纯情男孩
2.生成故事:
女孩playgirl心血来潮,向男孩innocent_boy发出恋爱邀请,说喜欢他,想和他在一起试一试(int playgirl_promise = 414)。
不过,因为playgirl就是爱玩的女孩子,所以很快新鲜感过了,不爱了,所以她当时的承诺不复存在了(自定义函数int* test_love(void)结束,playgirl_promise这一局部变量随之销毁)。
但是,innocent_boy对这个承诺念念不忘(return &playgirl_promise;int* innocent_boy = test_love();),还在对她说想要约定在一起一生一世,不过这当然没用了(*innocent_boy = 5201314不能间接改变playgirl_promise中的值)。
这是因为playgirl默认她疏远了,承诺也自然结束,就不该再打扰了(返回来的指针所指向的空间已释放,无法找到其原指向的变量(playgirl_promise)并改变);而innocent_boy却死心眼,过于深情,最后一无所有(innocent_boy沦为无意义的野指针)。
但如果playgirl能把分手说清楚,告诉男孩承诺已经不存在了(int* promise = test_love(); promise = NULL;),如果男孩不是恋爱脑到没救的话,会对此进行思考(做判断:if (promise != NULL)),不会再盲目打扰了(不判断就解引用:*innocent_boy = 5201314),也就不会一直无法move on了(被置为了空指针,至少不会沦为非法访问内存的野指针了)。
3.恋爱教训:
对于纯情的各位:在感情中不要对承诺太过当真,恋爱关系结束了,承诺也就烟消云散了,不应该对此念念不忘(避免返回局部变量的地址);
对于playgirl:希望你们良心未泯,把分手说的清楚一点,别让人留有幻想(指针指向的空间释放后,要及时置空指针)。
2.如何规避野指针
指针初始化
如果创建变量时,还没想好指针中要存放什么地址,可以先将其初始化为空指针(防止其沦为野指针)。
小心指针越界
注意边界条件的设置,不要越界访问。
当然也没那么严格, c语言规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。通俗来说,指针的比较可以往后超过一点,不构成危险,但是千万不能解引用来使用,用了就会造成数组越界的后果。
//符合标准(即向后超出的)
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0;
}
//不符合标准(向前超出的)
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
*vp = 0;
}
举列来说,上述第二个故事中指针over_male,可以与指向over_male[9]后面那部分内存空间的指针进行比较,因为没有使用,不会有危险;但是不能对其间接访问(如解引用后赋值),因为这会造成数组越界访问的后果。
(可以这么理解:毕竟本性难移,over_male可以继续保持自己的大男子主义,但是也要在意一下girl的感受,试探一下她的底线再定夺,有这样的想法但没被发现,也不至于会造成分手这种实质性的严重后果)
指针指向空间释放,及时置NULL
避免返回局部变量的地址
指针使用之前检查有效性
3, 4, 5放一起来说
例子就是上述第三个故事
3——再纯情,分手后都要把那些承诺忘记(指针指向空间释放),从零开始(置NULL )等下一个合适的人,为远离内耗忘得越快越好(及时);
4——playgirl分手时彻底一点,别给对方留有幻想( 避免返回局部变量的地址 );
5——别太恋爱脑,对方如果已经很决绝了,那么对此进行思考(指针使用之前检查有效性:if (promise != NULL)),别再盲目打扰了(不判断就解引用:*innocent_boy = 5201314),这样也就不会一直无法move on了(被置为了NULL,至少不会沦为非法访问内存的野指针了)。