这件事从一道面试题开始说起:
#include <string.h> #include <stdio.h> #include <stdlib.h> void getmemory(char*p)
{ p=(char *) malloc(100); strcpy(p,"hello world"); } int main( ) { char *str=NULL; getmemory(str); printf("%s ",str); free(str); return 0; }
一般人都知道这个程序是错误的,因为函数传参数是值拷贝的,所以函数调用结束以后str的值仍然是NULL,但是这里的str算是悬挂指针吗?它是野指针吗?对这个指针进行free能导致程序死亡吗?
答案是:这里的str不算是一个悬挂的野指针,因为它为NULL。
在说野指针的概念之前,先说一点计算机内存的管理,OS对内存进行管理,OS会记录每一个内存块是否有人在用。比如说有一个内存块刚刚被释放掉,OS就要将其回收,回收的过程很简单,把这个内存块从使用链表中挂到空闲链表中,代表这个内存块没人用了,下次有人申请的话,可以分配给内存申请者使用。但是这个刚刚被挂到空闲链表的内存块中的数据还在吗?答案是还在的。OS没有那闲功夫把这个内存数据块里的数据清空,OS的想法是谁申请,谁覆盖掉原来的数据就好了。
下面说一下悬挂野指针的本质:就是你的指针指向了OS已经放到空闲链表中的内存块。
这样是很危险的,因为这块数据本身就是垃圾,很不稳定,OS随时可能把这块内存块分配给其它的内存申请者使用而覆盖原本数据。
上面的例子程序中,str仅仅是一个空指针,它不指向任何的内存空间,所以对它free没有问题,因为free内部会检查,如果传入的指针是NULL,就直接返回了,不会做其它的操作。
但是如果这样的程序:
void getmemory(char* &p) { p=(char *) malloc(100); strcpy(p,"hello world"); } int main( ) { char *str = NULL; getmemory(str); printf("%s ",str); free(str); //str = NULL; free(str); return 0; }
str指向了内存的某块内存,连续对其释放两次,这就会使程序终止运行,因为你释放了OS空闲链表中的内存块。
但是如果你在第一次释放完成之后,紧接着把str的值置为NULL,那么下面再free都没有问题了,因为这个指针已经为NULL,它已经不再指向任何的内存空间,free函数将直接返回了。所以在内存空间释放掉之后,要紧接着把指向它的指针置为NULL,这是一个好的编程习惯。
关于内存释放摘一个感觉合适的例子:
分配一段内存就好像你去宾馆定房间,给你一个钥匙,还有房间,这两者配套。钥匙就是指针的值,那段内存就是房间。在你没有退房间时,宾馆不会再把这个房间分配给别人(在你没有释放指针时,系统就不会再把那段内存再次分配给别人了)。
释放一段内存就比较有意思了:就好像你去退房,宾馆就会在需要时把那个房间分给别的房客。但是有一点得注意:这时宾馆并不把你的钥匙收回。如果再次用你的那把钥匙去开那个房间(你的钥匙和房间还是配套的),会有两种情况:
1:房间还没有分配给别的房客,这时,OK,你还可以正常访问。
2:如果房间已经分配给别的房客,这时你再用你的那把钥匙去开门时,你就要挨板砖了,因为你侵入了别人的领地。
但也不是绝对的,如果你忘了东西在那个房间,如果你足够幸运那个房客,比较好心,没有动你的东西(没有修改那段内存),你甚至可以把它取出来。
也就是说,如果你释放了一个指针(严格地说应该是释放指针所指向的内存),会有不确定性,结果不好说。
所以,为了自己不去别人的内存拿出错误的数据,为了自己不修改别人的数据,为了不做一些违法的事情(大部分都是非故意做的),你在退房之后,系统不会回收你的房间的钥匙(指针变量的值),你自己还是乖乖的把那个钥匙丢掉吧(将指针值置为NULL)。