某些時候,咱們須要將指針賦值爲空指針,以防止野指針。
有人喜歡使用NULL做爲空指針常量使用,例如:int* p = NULL;。
也有人直接使用0值做爲空指針常量,例如:int* p = 0;。
前者可能以爲:NULL做爲空指針常量,名字很形象,可讀性較強。
後者可能以爲:NULL並非C/C++語言的關鍵字,而是一個在標準庫頭文件<stddef.h>中定義的宏,所以要使用NULL,可能須要直接或簡介地包含<stddef.h>頭文件,比較麻煩。
問題一:NULL與常數0值有何區別?
要弄清楚這個問題,咱們採用問與答的形式來描述。
問:NULL究竟是什麼?
答:NULL是一個宏。
問:它的值是多少?
答:C/C++標準規定:它的值是一個空指針常量(null pointer constant),由實現定義。
#1,#2
問:什麼樣的值才能稱之爲空指針常量?
答:C語言中常數0和(void*)0都是空指針常量;C++中(暫且忽略C++11)常數0是,而(void*)0 不是。
#3,#4
問:NULL宏是在哪裏定義的?
答:一般是在C標準庫的<stddef.h>頭文件中,不過別的頭文件中可能也有定義。
問:通常編譯器的<stddef.h>頭文件中NULL宏是如何定義的?
答:以gcc或clang編譯器爲例,NULL的定義大體以下(稍有簡化):
#if defined(__cplusplus)
# define NULL 0 // C++中使用0做爲NULL的值
#else
# define NULL ((void *)0) // C中使用((void *)0)做爲NULL的值
#endif
問:爲何C中(void*)0是空指針常量,而C++中不是?
答:由於C語言中任何類型的指針均可以(隱式地)轉換爲void*型,反過來也行,而C++中void*型不能隱式地轉換爲別的類型指針(例如:int*p = (void*)0;使用C++編譯器編譯會報錯)。
#5,#6
問:既然C/C++標準中,常數0均可做爲空指針常量,爲何不統一使用0?
答:我的以爲因爲(void*)0更能體現指針的意義,而常數0更多的時候是用做整數。所以,C語言中NULL定義選擇了(void*)0。(僅供參考)
問題二:C++11中爲何要引入nullptr?
考慮着這樣一個函數重載的情形:
#include <stddef.h>
void foo(int) {} // #1
void foo(char*) {} // #2
int main() {
foo(NULL); // 調用#1仍是#2?
}
從字面上來說,NULL是個空指針常量,咱們可能會以爲:既然是個指針,那麼應該調用#2。但事實上調用的倒是#1,由於C++中NULL擴展爲常數0,它是int型。
根本緣由就是:常數0既是整數常量,也是空指針常量。
爲了解決這種二義性,C++11標準引入了關鍵字nullptr,它做爲一種空指針常量。
#7例如:
void foo(int) {} // #1
void foo(char*) {} // #2
int main() {
foo(nullptr); // 它會毫無異議地調用#2
}
附註:
[#1] C99: 7.17-p3:
The macros are
NULL
which expands to an implementation-defined null pointer constant; and ...
[#2] C++03: 18.1-p4:
The macro NULL is an implementation-defined C + + null pointer constant in this International Standard(4.10).
[#3] C99: 6.3.2.3-p3:
An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.
[#4] C++03: 4.10-p1:
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero.
[#5] C99: 6.3.2.3-p1:
A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
[#6] C++03: 4.10-p2:
An rvalue of type 「pointer to cv T,」 where T is an object type, can be converted to an rvalue of type 「pointer to cv void.」
[#7] C++11: 4.10-p1:
A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t.
參考:
(1) C99/C++03/C++11標準文檔