C++中NULL和nullptr的區別

前言

C和C++的變量名是對大小寫敏感的,因此NULL和null並不是一回事,前者是C/C++中的系統關鍵字,null並不是。C++11以後又引入了nullptr,用以解決NULL在隱式轉換和作爲函數傳入參數時的二義性問題。


在C++11以前,在C/C++語言中,我們常常用NULL作爲指針變量的初始值。而在C++11之後,卻不建議你這麼做。

其實NULL根據命名全大寫可以看出來,它是一個常量,既然是常量,就需要進行宏定義。C語言的標準頭文件是這樣定義的

    #define NULL ((void*)0)

而到了C++中,則變成了

    #define NULL 0

查閱stddef.h,可以看到如下定義

#undef NULL 
#if defined(__cplusplus) 
#define NULL 0 
#else 
#define NULL ((void *)0) 
#endif

從定義中可以看出,C++中,NULL其實就是0,但是也可以用作空指針,只是用作空指針可能是爲了兼容C,迫於無奈。

以下一段代碼可以很好地解釋NULL存在的問題:

#include<iostream> 
using namespace std; 
void test(void *p) 
{ 
    cout<<"p is pointer "<<p<<endl; 
} 
void test(int num) 
{ 
    cout<<"num is int "<<num<<endl; 
} 
int main(void) 
{ 
    test(NULL); 
    return 0; 
}

這時,如果編譯的話,會報以下錯誤,

$ g++ -o test test.cpp main.cpp: 
    In function ‘int main()’: 
    main.cpp:14:14: error: call of overloaded ‘test(NULL)’ is ambiguous test(NULL);

很明顯,NULL存在二義性,它既是整數,也是一個指針,函數test()無法根據參數的數據類型判斷應該調用哪一個實現。

這時使用nullptr的優越性就體現出來了,因爲它可以很好地把空指針這一層意思給剝離出來。nullptr就是C++11爲了解決這個痛點而推出的東西。

test(nullptr);

就會自然而然地走到指針的那個函數裏。因此,以後若想使用整數特性,就賦值爲0,若想使用指針特性,就賦值爲nullptr,這樣一目瞭然,減少了未知的Bug的可能性。

多說一句,爲什麼要作此改動,我想首先應該是C和C++在處理void *類型的時候存在一定的區別。C語言中,void* 類型的變量可以賦值給任意類型的指針,也可以被任意類型的指針賦值,兩個方向都不會報錯。但是C++具有更嚴格的類型檢查,前者是不被允許的。

因此下面一段C語言代碼是可以編譯通過的

int main() 
{ 
    void* a; 
    int* b=a; 
}

但是下面的C++代碼就會報錯

test.cpp:4:7: 
    error: cannot initialize a variable of type 'int *' with an lvalue of type 'void *' int* b=a;

與此同時,在malloc上,也存在類似的問題:

int len = 100; int p = malloc(len * sizeof(int)); // C推薦做法 
int p = (int )malloc(len * sizeof(int)); // C++推薦做法

malloc函數返回值得類型是 void*,C不要求強制類型轉換,會自動進行隱式轉換,但是C++則需要,因爲void* 不能轉換成其他類型的指針。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章