一:野指針
“野指針”不是NULL指針,是指向“垃圾”內存的指針。
“野指針”的成因主要有兩種:
(1)指針變量沒有被初始化。任何指針變量剛被創建時不會自動成為NULL指針,它的缺省值是隨機的,它會亂指一氣。所以,指針變量在創建的同時應當被初始化,要麼將指針設置為NULL,要麼讓它指向合法的內存。例如
(2)指針p被free或者delete之後,沒有置為NULL,讓人誤以為p是個合法的指針。
free和delete只是把指針所指的內存給釋放掉,但並沒有把指針本身干掉。free以後其地址仍然不變(非NULL),只是該地址對應的內存是垃圾,p成了“野指針”。如果此時不把p設置為NULL,會讓人誤以為p是個合法的指針。
如果程序比較長,我們有時記不住p所指的內存是否已經被釋放,在繼續使用p之前,通常會用語句if (p != NULL)進行防錯處理。很遺憾,此時if語句起不到防錯作用,因為即便p不是NULL指針,它也不指向合法的內存塊。
}
(3)指針操作超越了變量的作用范圍。這種情況讓人防不勝防,示例程序如下:
{
public:
};
{
}
}
函數Test在執行語句p->Func()時,對象a已經消失,而p是指向a的,所以p就成了“野指針”。
2:空指針/0/NULL
空指針是一個被賦值為0的指針,在沒有被具體初始化之前,其值為0.
NULL 是一個標准規定的宏定義,用來表示空指針常量。
#define NULL 0
#define NULL ((void*)0)
判斷一個指針是否為空指針:
f(!p) 和
最好使用後兩種,有些平台NULL不是0,這時候程序就會有問題了。
其中if(NULL == p) 與if(p == NULL) 沒有區別,前一種是避免錯誤的寫法(後面的容易寫成P=NULL,編譯器不能發現。而前面的寫成NULL=p時會編譯不過)。
一般在使用指針前(特別是對其進行加減)要對其進行判斷 if(p == NULL) ,
如:
Item* pItem = itemList.getItem(index);
Item* ItemList::getItem(int index)
{
}
如果返回的是空指針,且後面對pItem做了相關的操作,會有空指針異常,程序可能會崩潰。
調用free(p)函數後應對p置空,即p=NULL。
3:VOID類型
void的字面意思是“無類型”,void *則為“無類型指針”,void *可以指向任何類型的數據。
void幾乎只有“注釋”和限制程序的作用,因為從來沒有人會定義一個void變量,讓我們試著來定義:
void a;
這行語句編譯時會出錯,提示“illegal use of type 'void'”。不過,即使void a的編譯不會出錯,它也沒有任何實際意義。
void真正發揮的作用在於:
(1) 對函數返回的限定;
(2) 對函數參數的限定。
眾所周知,如果指針p1和p2的類型相同,那麼我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數據類型,則必須使用強制類型轉換運算符把賦值運算符右邊的指針類型轉換為左邊指針的類型。
例如:
float *p1;
int *p2;
p1 = p2;
其中p1 = p2語句會編譯出錯,提示“'=' : cannot convert from 'int *' to 'float *'”,必須改為:
p1 = (float *)p2;
而void *則不同,任何類型的指針都可以直接賦值給它,無需進行強制類型轉換:
void *p1;
int *p2;
p1 = p2;
但這並不意味著,void *也可以無需強制類型轉換地賦給其它類型的指針。因為“無類型”可以包容“有類型”,而“有類型”則不能包容“無類型”。道理很簡單,我們可以說“男人和女人都是人”,但不能說“人是男人”或者“人是女人”。下面的語句編譯出錯: