一、悬垂指针

    当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针)。若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,则将产生无法预料的后果。因为此时迷途指针所指向的内存现在包含的已经完全 是不同的数据。通常来说,若原来的程序继续往迷途指针所指向的内存地址写入数据,这些和原来程序不相关的数据将被损坏,进而导致不可预料的程序错误。这种类型的程序错误,不容易找到问题的原因,通常会导致段错误(Linux系统中)和一般保护错误(Windows系统中)。如果操作系统的内存分配器将已经被覆盖的数据区域再分配,就可能会影响系统的稳定性。

    某些编程语言允许未初始化的指针的存在,而这类指针即为野指针。野指针所导致的错误和迷途指针非常相似,但野指针的问题更容易被发现。

二、悬垂指针的成因

 

在许多编程语言中(比如C),显示地从内存中删除一个对象或者返回时通过销毁栈帧,并不会改变相关的指针的值。该指针仍旧指向内存中相同的位置,即使引用已经被删除,现在可能已经挪作他用。

一个简单的例子:

{   
char *dp = NULL;  
/* ... */
 {       
char c;      
 dp = &c; 
  } /* c falls out of scope *//* dp is now a dangling pointer */
}

上述问题的解决方法是在该部分程序退出之前立即给CP赋0值(NULL)。另一个办法是保证CP在没有初始化之前,将不再被使用。

迷途指针经常出现在混杂使用malloc() 和 free() 库调用: 当指针指向的内存释放了,这时该指针就是迷途的。和前面的例子一样,一个避免这个错误的方法是在释放它的引用后将该指针的值重置为NULL,如下所示:

#include <stdlib.h>

{

    char *cp =malloc ( A_CONST );

    /* ... */

    free ( cp);      /* cp now becomes a danglingpointer */

    cp = NULL;        /* cp is no longer dangling */

    /* ... */

}

有个常见的错误是当返回一个基于栈分配的局部变量的地址时,一旦调用的函数返回,分配给这些变量的空间将被回收,此时它们拥有的是"垃圾值"。

int * func ( void )

{

    int num = 1234;

    /* ... */

    return #

}

在调用func之后一段时间,尝试从该指针中读取num的值,可能仍然能够返回正确的值(1234),但是任何接下来的函数调用会覆盖原来的栈为num分配的空间。这时,再从该指针读取num的值就不正确了。如果要使一个指向num的指针都返回正确的num值,则需要将该变量声明为static。

三、野指针的产生

野指针指的是还没有初始化的指针。严格地说,编程语言中每个指针在初始化前都是野指针。

 

一般于未初始化时便使用指针就会产生问题。大多数的编译器都能检测到这一问题并警告用户。

  1. int f(int i)  

  2. {  

  3. char* cp;   //cp is a wild pointer

  4. static char*scp;  //scp is not a wild pointer: staticvariables are initialized to 0

  5. //at start and retain their values from the last call afterwards.

  6. //Using this feature may be considered bad style if not commented

  7. }  

cp是一个野指针,scp不是野指针。静态变量一开始被初始化为0,从最后一次调用后保持着它们的值。如果没有注释,使用这个特性也许被视为不良风格。
四、避免悬垂指针的产生
    在 C/C++ 中,一种最简单的技术是实现一个 free()(或类似的)替代版本或者 delete 析构器来保证指针的重置。然后,这个技术不会清除其他指针变量,它们含有该指针的副本。
/* Alternative version for  free*/
void safefree(void **pp)
{
    if(pp!=NULL){  /* safe check*/
    free(*pp);    /*deallocate chunk,note that free(NULL) is valid*/
    *pp=NULL;    /* reset original pointer*/
}
int function(int i)
{
    char *p=NULL,*p2;
    p=(char*)malloc(1000); /*get a chunk*/
    p2=p;                /*copy the pointer*/
    /*use the chunk here*/
    safefree(&p);   /*safety freeing;does not effect p2 variable*/
    safefree(&p);  /*this second call won't fail*/
    char c=*p2;/*p2 is still a dangling pointer ,so this is undefined behavior*/
野指针   悬垂指针   迷途指针_稳定性}
替换版本可以用来保证在调用 malloc() 之前一个空指针的正确性:
safefree(&p);        /* i'm not sure if chunk has been released */
= malloc(1000);    /* allocate now */

这些用法可以通过 #define 指令来构造有用的宏指令,创建像元语言的东西来掩饰或者被嵌入到一个工具库中。但凡使用这个技术的程序员在会用到 free()的地方应该使用安全版本;不这么做会再次导致这些问题。另外,这个解决方案局限于单个程序或工程的作用域中,并且应该正确地写入文档。

在更多结构化的解决方案中,一种流行的避免悬垂指针的技术是使用智能指针。一个智能指针通常使用引用技术来收回对象。还有些技术包括 tombstones 方法和 locks-and-keys 方法。

另一个方法是使用 Boehm 垃圾收集器,一种保守的垃圾收集器,取代C和C++中的标准内存分配函数。此法通过禁止内存释放函数来完全消除悬垂指针引发的错误,通过收集垃圾来回收对象。

像Java语言,悬垂指针这样的错误是不会发生的,因为Java中没有明确地重新分配内存的机制。而且垃圾回收器只会在对象的引用数为0时重新分配内存。

 

五、悬垂指针的检测

为了能发现迷途指针,一种普遍的编程技术——一旦指针指向的内存空间被释放,就立即把该指针置为空指针或者为一个非法的地址。

当空指针被重新引用时,此时程序将会立即停止,这将避免数据损坏或者某些无法预料的后果。这将使接下来的编程过程产生的错误变得容易发现和解决了。

这种技术在该指针有多个复制时就无法起到应有的作用了。