关于野指针的一些问题与总结

void Test(void)
{
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);    
if(str != NULL)
{
  strcpy(str, “world”);
  printf(str);
}
复制代码

 请问运行Test函数会有什么样的结果?
答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str != NULL)语句不起作用。


1.何为野指针?

野指针指指向一个已删除的对象或未申请访问受限内存区域的指针。与空指针不同,野指针无法通过简单地判断是否为NULL避免,而只能通过养成良好的编程习惯来尽力减少。对野指针进行操作很容易造成程序错误。

解释一下:

malloc 和 free 是在系统的堆上分配空间。
malloc是申请,意思就是告诉系统,我要用一块RAM,给我用了别人就不要用了。
free是释放,意思是告诉系统,给我的这块RAM我用完了,不再用了,系统可以把它干别的了。free()释放的是指针指向的内存。并不是说,free()解除了原来指向这块内存的指针与这块内存地址之间的联系,联系依然存在,这就可能导致误用出错,所以需要在free之后置NULL,这样就解除关联了。举个例子就是,A跟妻子离婚了,但A和她曾经是夫妻,曾经关系很亲密,这种关系不会随着离婚而改变,这是客观的,离婚了即free,A不能再去骚扰前妻,否则就是违法,A要是再想干那啥,A会坐牢的!!!
free之后,系统还没有拿这块RAM干别的事之前,这块RAM的内容可能是不会变的,依然可以读出原来的内容,因为你的指针a还是指向这块RAM。但要注意,这块RAM已经不属于你了,读一下内容无所谓,如果往里面写就很危险了。

2.成因

通俗说法:

 如果程序定义了一个指针,就必须要立即让它指向一个我们设定的空间或者把它设为NULL,如果没有这么做,那么这个指针里的内容是不可预知的,即不知道它 指向内存中的哪个空间(即野指针),它有可能指向的是一个空白的内存区域,可能指向的是已经受保护的区域,甚至可能指向系统的关键内存,如果是那样就糟 了,也许我们后面不小心对指针进行操作就有可能让系统出现紊乱,死机了。所以我们必须设定一个空间让指针指向它,或者把指针设为NULL,这是怎么样的一 个原理呢,如果是建立一个与指针相同类型的空间,实际上是在内存中的空白区域中开辟了这么一个受保护的内存空间,然后用指针来指向它,那么指针里的地址就 是这个受保护空间的地址了,而不是不可预知的啦,然后我们就可以通过指针对这个空间进行相应的操作了;如果我们把指针设为NULL,我们在头文件定义中的 #define NULL 0 可以知道,其实NULL就是表示0,那么我们让指针=NULL,实际上就是让指针=0,如此,指针里的地址(机器数)就被初始化为0了,而内存中地址为0 的内存空间……不用多说也能想象吧,这个地址是特定的,那么也就不是不可预知的在内存中乱指一气的野指针了。

  还应该注意的是,free和delete只是把指针所指的 内存给释放掉,但并没有把指针本身干掉。指针p被free以后其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,p成了“野指针”。如果此时不 把p设置为NULL,会让人误以为p是个合法的指针。用free或delete释放了内存之后,就应立即将指针设置为NULL,防止产生“野指针”。内存 被释放了,并不表示指针会消亡或者成了NULL指针。(而且,指针消亡了,也并不表示它所指的内存会被自动释放。)

指针变量未初始化

任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

指针释放后之后未置空

有时指针在free或delete后未赋值 NULL,便会使人以为是合法的。别看free和delete的名字(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生“野指针”。

指针操作超越变量作用域

不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。示例程序如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A {
public:
  void Func(void){ cout << “Func of class A” << endl; }
};
class B {
public:
  A *p;
  void Test(void) {
    A a;
    p = &a; // 注意 a 的生命期 ,只在这个函数Test中,而不是整个class B
  }
  void Test1() {
  p->Func(); // p 是“野指针”
  }
};
函数 Test1 在执行语句 p->Func()时,p 的值还是 a 的地址,对象 a 的内容已经被清除,所以 p 就成了“野指针” 。

3.规避

在养成这些习惯的情况下,野指针的危害是可以降低的:
如:int* pInteger=NULL;//指针定义处
...
if(pInteger != NULL)
{
free(pInteger);
pInteger=NULL;//指针释放之后并不为空,要设置其为空
}
pInteger=(int*)malloc(10*sizeof(int));
if(pInteger != NULL)
{
printf("内存申请成功/n!");
exit(0);
}
...

初始化时置 NULL

指针变量一定要初始化为NULL,因为任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的。

释放时置 NULL

当指针p指向的内存空间释放时,没有设置指针p的值为NULL。delete和free只是把内存空间释放了,但是并没有将指针p的值赋为NULL。通常判断一个指针是否合法,都是使用if语句测试该指针是否为NULL。例如:
1
2
3
4
5
6
7
8
9
int *p=newint(6);
delete p;
// 应加入 p=NULL; 以防出错
// ...
if(p != NULL)
{
  *p=7;
  cout << p << endl;
}
对于使用 free 的情况,常常定义一个宏或者函数 xfree 来代替 free 置空指针:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define xfree(x) free(x); x = NULL;
// 在 C++ 中应使用 nullptr 指代空指针
// 一些平台上的 C/C++ 已经预先添加了 xfree 拓展,如 GNU 的 
 
libiberty
xfree(p);
// 用函数实现,例如 GitHub 上的 AOSC-Dev/Anthon-Starter #9:
static inline void *Xfree(void *ptr) {
    free(ptr);
#ifdef __cplusplus
    return nullptr;
#else
    return NULL;
#endif
}
q=Xfree(q);
所以动态分配内存后,如果使用完这个动态分配的内存空间后,必须习惯性地使用delete操作符取释放它。

关于函数使用需要注意的一些地方: 

A、申请了内存空间后,必须检查是否分配成功。 

B、当不需要再使用申请的内存时,记得释放;释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它。 

C、这两个函数应该是配对。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会 

出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。 

D、虽然malloc()函数的类型是(void *),任何类型的指针都可以转换成(void *),但是最好还是在前面进行强制类型转换,因为这样可以躲过一 

些编译器的检查。 

4.相关链接

http://www.cnblogs.com/viviwind/archive/2012/08/14/2638810.html

http://blog.chinaunix.net/uid-24227137-id-3270110.html








阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页