nullptr、NULL、null和0
C語言和C++對大小寫是敏感的,也就是說null和NULL是區別對待的。NULL代表空地址,null只是一個符號,null可以是自己定義的一個變量。
NUll是一個宏定義 #define NULL 0,容易產生宏常常產生的錯誤。
nullptr是C++11中才引入的一個字面值常量,可以被轉換成任意其他指針,其用法和直接用字面值0是一樣的。
以下不完全轉載至http://www.kiford.com/a/WZ012116VDNEF3BD?utm_source=tuicool&utm_medium=referral
在強調一下,NULL 在C++中的定義僅僅是 0,僅此而已。(切記,NULL 是宏,以下錯誤都是宏引起的)
當然,這看起來僅僅是語法問題啦。那么使用 nullptr 和 NULL 究竟有沒有差別呢?當然有! 使用 nullptr 可以幫助我們避免各式各樣的錯誤。
還是用實例來說明吧。
假設,有兩個被重載的函數:
void Foo(int x, int y, const char *name);
void Foo(int x, int y, int ResourceID);
我們可能像下面這樣來調用:
1.Foo(1, 2, NULL);
有些程序猿認為這樣寫調用的是第一個函數。結果卻不是這樣的,因為 NULL 僅僅就是 0,是int類型的 0,所以第2個函數會被調用,而不是第一個函數。
然而,如果使用的是 nullptr,那么第一個函數將被調用,就不會產生這種錯誤了。
另一個通常的使用 NULL 方式如下代碼所示:
if (unknownError)
throw NULL;
依我之見,拋出一個指針異常這件事兒就是令人懷疑的。不過有時一些人會這么做。顯然,開發者需要編寫這樣的代碼。好吧,討論這樣做的好與壞超出了本文的范疇。
重點是我們想在產生未知錯誤時拋出一個異常,并且要把一個空指針"送到"外部世界。但實際上送出去的不是指針,而是一個int類型。結果就是異常并不會像我們認為的那樣被捕獲 。
throw nullptr; 可以讓我們避免這種錯誤。但,好吧,這并不意味著我完全接受這種(拋出指針異常)的代碼。
在一些情況下,使用 nullptr 是會產生編譯錯誤的。
例如一些WinApi函數返回 HRESULT 類型的參數。HRESULT 類型和指針本沒什么關系,然而如下所示的這種渣渣代碼卻有可能被寫出來:
if (WinApiFoo(a, b, c) != NULL)
編譯沒有問題,因為 NULL 是int類型的0,HRESULT 是long型,比較類型int和long是可以的。如果使用 nullptr 呢,如下所示的代碼就編譯不過去啦:
if (WinApiFoo (a, b, c)! = nullptr)
如此,這個錯誤便能夠被立刻被發現并改正啦。
你肯定有這種想法,這種例子是有很多,但這都是你自己臆想的啊,這可不能說服大家。有沒有實際的例子呢?當然有,這就有一個,只不過不是那種短小精悍的例子。
這段代碼來自 MTASA。
有這么一個 RtlFillMemory() ,它可能是一個函數或者宏,這都不重要。它和 memset() 有點像,只不過第二個和第三個參數是顛倒的。他可能是這樣聲明的:
#define RtlFillMemory(Destination,Length,Fill)
memset((Destination),(Fill),(Length))
還有一個 FillMemory(),就是把 RtlFillMemory() 重定義了一下:
#define FillMemory RtlFillMemory
好吧,挺長挺亂的,但這確實是實際中的一個例子。
FillMemory 是這么被使用的:
LPCTSTR __stdcall GetFaultReason ( EXCEPTION_POINTERS * pExPtrs ){
....
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)&g_stSymbol ;
FillMemory ( pSym , NULL , SYM_BUFF_SIZE ) ;
....
}
這段代碼bug不止一個。明顯的就是第2個參數和第3個參數用反了。這就是分析器報了兩個警告的原因 V575:
V575 The 'memset' function processes value '512'. Inspect the second argument. crashhandler.cpp 499 (memset 處理的值是'512',檢查一下第2個參數)
V575 The 'memset' function processes '0' elements. Inspect the third argument. crashhandler.cpp 499(memset 處理了0個元素,檢查一下第1個參數)
但錯誤可不止這一處:NULL 用在這壓根就不合適。memset() 函數是按字節填充的,所以使用 NULL 值來填充的想法就是錯(譯者注:雖然 memset 的第2個參數value是int型,但實現時會轉化成 unsigned char 來填充內存,參看memset文檔)。 來看看正確的代碼:
FillMemory(pSym, SYM_BUFF_SIZE, 0);
或者這樣:
ZeroMemory(pSym, SYM_BUFF_SIZE);
但這也不是重點,重點是這種渣渣代碼居然能
編~譯~成~功~。可是如果程序猿們習慣使用的是 nullptr 而不是 NULL,那么代碼就會寫成這樣:
FillMemory(pSym, nullptr, SYM_BUFF_SIZE);
這樣的話編譯器會拋出一個編譯錯誤,猿們就能意識到他們的錯了,就會多多注意他們的代碼了。
另外,我明白這種情況下不應該責備 NULL 本身,這只能怪代碼編譯時沒有拋出關于 NULL 的警告,我們的代碼應該盡可能讓編譯器來發現錯誤。
總結
以上是生活随笔為你收集整理的nullptr、NULL、null和0的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: postgresql数据库网络地址存储探
- 下一篇: 图解服务器硬盘接口