野指針   懸垂指針   迷途指針

 

 一、懸垂指針

    當所指向的對象被釋放或者收回,但是對該指針沒有作任何的修改,以至於該指針仍舊指向已經回收的內存地址,此情況下該指針便稱懸垂指針(也叫迷途指針)。若操作系統將這部分已經釋放的內存重新分配給另外一個進程,而原來的程序重新引用現在的迷途指針,則將產生無法預料的後果。因爲此時迷途指針所指向的內存現在包含的已經完全 是不同的數據。通常來說,若原來的程序繼續往迷途指針所指向的內存地址寫入數據,這些和原來程序不相關的數據將被損壞,進而導致不可預料的程序錯誤。這種類型的程序錯誤,不容易找到問題的原因,通常會導致段錯誤(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時重新分配內存。

 

五、懸垂指針的檢測

爲了能發現迷途指針,一種普遍的編程技術——一旦指針指向的內存空間被釋放,就立即把該指針置爲空指針或者爲一個非法的地址。

當空指針被重新引用時,此時程序將會立即停止,這將避免數據損壞或者某些無法預料的後果。這將使接下來的編程過程產生的錯誤變得容易發現和解決了。

這種技術在該指針有多個複製時就無法起到應有的作用了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章