C/C++雜記:NULL與0的區別、nullptr的來歷

某些時候,咱們須要將指針賦值爲空指針,以防止野指針。
 
有人喜歡使用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標準文檔
 
相關文章
相關標籤/搜索