對於空(null)指標與 NULL 指標,相信許多讀者對它們之間的關係都很迷惑,甚至有很大一部分讀者會認為它們根本就是一回事。其實不然,它們之間存在著一定的本質區別,下面就來詳細闡述它們之間的不同。
對於空(null)指標的概念,在 C 標準中明確地定義:值為 0 的整型常數表示式,或強制(轉換)為“void*”型別的此類表示式,稱為
空指標常數。當將一個空指標常數賦予一個指標或與指標作比較時,將把該常數轉換為指向該型別的指標,這樣的指標稱為
空指標。
空指標在與指向任何物件或函數的指標作比較時保證不會相等。
根據上面的定義,我們可以對空指標做如下幾點剖析:
1) 每一種指標型別都有一個空指標,它與同型別的其他所有指標值都不相同。
2) 由系統保證空指標不指向任何實際的物件或函數,也就是說,任何物件或者函數的地址都不可能是空指標,空指標與任何物件或函數的指標值都不相等。因此,取地址操作符 & 永遠也不能得到空指標,同樣對 malloc() 函數的成功呼叫也不會返回空指標,但如果呼叫失敗,則 malloc() 函數返回空指標。
3) 空指標表示“未分配”或者“尚未指向任何地方”。它與未初始化的指標有所不同,空指標可以確保不指向任何物件或函數,而未初始化指標可能指向任何地方。
4) 0、0L、''、3-3、0*17以及(void*)0等都是空指標常數,則:
int *p;
p=0;
/*或者*/
p=0l;
/*或者*/
p='';
/*或者*/
p=3-3;
/*或者*/
p=0*17;
/*或者*/
p=(void*)0;
指標變數 p 經過上面任何一種賦值操作之後都將成為一個空指標。至於編譯時系統究竟選取哪種形式作為空指標常數使用,則與具體實現相關。在一般情況下,對於 C 語言系統,選擇“(void*)0”或 0 的居多(也有個別的選擇 0L);而對於 C++ 語言系統,由於存在嚴格的型別轉化的要求,“void*”不能像在 C 語言中那樣自由轉換為其他指標型別,所以通常只選 0 作為空指標常數,而不選擇“(void*)0”。
5) 對於空指標究竟指向記憶體的什麼地方,在標準中並沒有明確規定。也就是說,用哪個具體的地址值(0 地址還是某一特定地址)來表示空指標完全取決於系統的實現。在一般情況下,空指標指向 0 地址,即空指標的內部用全 0 來表示,也可以稱它為
零空指標。當然,也有一些系統用一些特殊的地址值或特殊的方式來表示空指標,也可以稱它為
非零空指標。
但在實際程式設計中,我們並不需要了解在系統上的空指標到底是一個零空指標還是一個非零空指標。而我們僅僅只需要知道一個指標是否是空指標就可以了,編譯器會自動實現其中的轉換,為我們遮蔽其中的實現細節。因此,千萬不要把空指標的內部表示等同於整數0的物件表示,有時它們是不同的。
在了解空指標的概念之後,下面來看 NULL 指標。
作為一種良好的程式設計習慣,很多程式設計師都不願意在程式中到處出現未加修飾的 0 或者其他空指標常數。為了讓程式中的空指標使用更加明確,從而保持統一的程式設計風格,標準 C 專門定義了一個標準預處理宏 NULL,其值為“空指標常數”,通常是 0 或者“((void*)0)”,即在指標上下文中的 NULL 與 0 是等價的,而未加修飾的 0 也是完全可以接受的。如在 VC++ 中定義預處理宏 NULL 的程式碼如下:
/* Define NULL pointer value */
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
這裡需要說明的是,當 NULL 定義為“((void *)0)”時,即 NULL 是可以賦值給任何型別指標的值,它的型別為 void*,而不是整數 0,因此初始化“FILE*fp=NULL;”是完全合法的。
而為了區分整數 0 和空指標 0,當需要其他型別的 0 時,即使可能工作,也不能使用 NULL,因為這樣處理其格式是錯誤的,這種型別在非指標上下文中是不能工作的。特別需要注意的是,不能在需要 ASCII 空字元(NUL)的地方使用 NULL。如果確實需要,則可以自定義為:
#define NUL ''
由此可見,常數 0 是一個空指標常數,而 NULL 僅僅是它的一個別名。NULL 可以確保是 0,但空(null)指標卻不一定。