某些時候,我們需要將指針賦值為空指針,以防止野指針。
有人喜歡使用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標准文檔
(2) nullptr提案文檔:N2431:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf