nullptr、NULL、null和0

C语言和C++对大小写是敏感的,也就是说nullNULL是区别对待的。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类型的0HRESULT long型,比较类型intlong是可以的。如果使用 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个参数valueint型,但实现时会转化成 unsigned char 来填充内存,参看memset文档)。 来看看正确的代码:

FillMemory(pSym, SYM_BUFF_SIZE, 0);

 
 

或者这样:

ZeroMemory(pSym, SYM_BUFF_SIZE);

 
 

但这也不是重点,重点是这种渣渣代码居然能
编~译~成~功~。可是如果程序猿们习惯使用的是 nullptr 而不是 NULL,那么代码就会写成这样:

FillMemory(pSym, nullptr, SYM_BUFF_SIZE);

 
 

这样的话编译器会抛出一个编译错误,猿们就能意识到他们的错了,就会多多注意他们的代码了。

另外,我明白这种情况下不应该责备 NULL 本身,这只能怪代码编译时没有抛出关于 NULL 的警告,我们的代码应该尽可能让编译器来发现错误。

原文链接: https://www.cnblogs.com/Yan47/p/5968676.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

    nullptr、NULL、null和0

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/242275

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月13日 下午10:14
下一篇 2023年2月13日 下午10:16

相关推荐