您是否在C ++中使用NULL或0(零)作为指针?


192

在C ++的早期,当它被拴在C之上时,您不能使用NULL,因为它被定义为(void*)0。您不能将NULL分配给除以外的任何指针void*,这使它毫无用处。那时,您已经接受过将0零指针用于空指针。

直到今天,我仍继续使用零作为空指针,但周围的人坚持使用NULL。我个人认为将名称(NULL)赋予现有值没有任何好处-并且由于我也喜欢将指针作为真值进行测试:

if (p && !q)
  do_something();

那么使用零会更有意义(例如,如果您使用NULL,则不能在逻辑上使用p && !q-您需要与进行显式比较NULL,除非您假设NULL为零,在这种情况下,请使用NULL)。

是否有客观的理由更喜欢零而不是NULL(反之亦然),或者仅仅是个人喜好?

编辑:我应该添加(并最初要说),对于RAII和异常,我很少使用零/ NULL指针,但是有时您仍然需要它们。


9
等等,是否将null指针赋值为false而不管内部的null是否为零?
Mooing Duck,

Answers:


185

这是Stroustrup对此的看法:C ++样式和技术常见问题解答

在C ++中,的定义NULL为0,因此仅存在美学差异。我更喜欢避免使用宏,因此我使用0。另一个问题NULL是,人们有时会错误地认为它不同于0和/或不是整数。在标准前代码中,NULL有时被定义为不合适的东西,因此必须避免。这些天来这种情况不太常见。

如果必须命名空指针,请调用它nullptr;这就是C ++ 11中的名称。然后,nullptr将是一个关键字。

就是说,不要汗流the背。


7
Bjarne在C ++ 0x开始处理新的null类型之前就写了这个。可以在平台上使用NULL的情况将是这种情况,我认为您会在对此达成的普遍共识中看到C更改。
理查德·科登,

122

我认为有一些论点(其中一个是相对较新的论点)与Bjarne在此问题上的立场相矛盾。

  1. 意向文件

using NULL允许对其使用进行搜索,它还突出显示了开发人员想要使用NULL指针,而不管编译器是否对其进行了解释NULL

  1. 指针和“ int”的重载相对罕见

大家引用的示例是:

void foo(int*);
void foo (int);

void bar() {
  foo (NULL);  // Calls 'foo(int)'
}

但是,至少在我看来,上述问题不是因为我们对空指针常量使用了NULL,而是因为我们有'foo'的重载,而这些重载采用了非常不同的参数。该参数也必须是int,因为任何其他类型都将导致模棱两可的调用,因此会生成有用的编译器警告。

  1. 分析工具今天可以提供帮助!

即使在没有C ++ 0x的情况下,今天也有可用的工具可以验证NULL是否正在将指针用于指针,并将0其用于整数类型。

  1. C ++ 11将具有一种新std::nullptr_t类型。

这是该表的最新参数。的问题0,并NULL正在积极处理的的C ++ 0x,你可以保证每个提供实施NULL,第一件事,他们需要做的是:

#define NULL  nullptr

对于那些使用NULL而不是使用的人0,更改将是对类型安全性的改进,而无需付出任何努力或什至无需付出任何努力-如果有的话,它也可能会在他们曾经使用NULL过的地方发现一些错误0。对于0今天使用...的任何人...希望...他们希望他们对正则表达式有很好的了解...


1
我必须承认,这些是相当不错的一点。我很高兴C ++ 0x将具有null类型,我认为这会使很多东西变得更干净。
罗布

2
@理查德,为什么不相反呢?您可以使用Meyers nullptr_t,然后在0x可用时将其移除#include并一直保持安全。
fnieto-Fernando Nieto

15
#define NULL nullptr似乎很危险。不管是好是坏,许多遗留代码对非0的东西都使用NULL。例如,句柄通常被实现为某种整数类型,将其设置NULL为并不少见。我什至看到过滥用情况,例如NULL用来将a设置char为零终止符。
Adrian McCarthy

8
@AdrianMcCarthy:我只能说,如果有代码静默编译并具有不同含义的危险,那是危险的。我非常确定情况并非如此,因此实际上会检测到所有NULL的不正确使用。
理查德·科登

3
@RichardCorden:嗯,假定这些的其他用途NULL实际上是不正确的。许多API长期以来都NULL与句柄一起使用,实际上是其中许多文件的用法说明。突然破坏它们并声明他们做错了不是实用的。
阿德里安·麦卡锡

45

使用NULL。NULL显示您的意图。它为0是无关紧要的实现细节。


28
0不是实现细节。该标准将0定义为表示空指针的任何位模式。
Ferruccio

5
好像.. !! 杜德(Dude),C ++是一种低级语言!使用0,这是一个众所周知的习惯用法。
哈森

8
我了解这是标准的一部分。就阅读代码而言,这是一个实现细节。读者应该认为“ NULL指针”不是“ 0,在这种情况下,它表示NULL指针,而不是我可能要进行算术运算的数字”。
安迪·莱斯特

2
+1。同意安迪。@Ferruccio,程序员想法的实现细节与编译器的实现定义不同
用户

如果您使用NULL,则在没有复杂头的纯代码中,您会发现“在此范围内未定义NULL”的错误
ArtificiallyIntelligence

37

我一直使用:

  • NULL 对于指针
  • '\0' 对于字符
  • 0.0 花车和双打

那里0会很好。这是发信号意图的问题。话虽如此,我对此并不满意。


24
ya可能应该对浮点数使用0.0F,以避免隐式类型转换
EvilTeach

35

很久以前,我就停止使用NULL来支持0(以及其他大多数宏)。我这样做不仅是因为我想尽可能避免使用宏,还因为NULL在C和C ++代码中似乎已被过度使用。它似乎仅在需要0值时使用,而不仅用于指针。

在新项目上,我将其放在项目标题中:

static const int nullptr = 0;

现在,当符合C ++ 0x的编译器问世时,我要做的就是删除该行。这样的好处是Visual Studio已经将nullptr识别为关键字并适当突出显示了它。


4
从长远来看,使用NULL将更具可移植性。“ nullptr”将在某些平台上可用,而在其他平台上不可用。这里的解决方案要求您在声明周围使用预处理器,以确保仅在需要时使用预处理器。NULL将自动执行此操作。
理查德·科登,

6
我不同意。在短期内,直到编译器赶上来之前,它的可移植性将降低。从长远来看,它将是可移植的,并且可能更具可读性。
Ferruccio

4
另外,对于非C ++ 0x编译器,您始终可以#define nullptr NULL。
09年

20
    cerr << sizeof(0) << endl;
    cerr << sizeof(NULL) << endl;
    cerr << sizeof(void*) << endl;

    ============
    On a 64-bit gcc RHEL platform you get:
    4
    8
    8
    ================

这个故事的主旨。处理指针时,应使用NULL。

1)它声明了您的意图(不要让我搜索所有代码以试图弄清变量是指针还是某种数字类型)。

2)在某些需要可变参数的API调用中,它们将使用NULL指针来指示参数列表的结尾。在这种情况下,使用“ 0”而不是NULL可能会导致问题。在64位平台上,va_arg调用需要一个64位指针,但是您只传递32位整数。在我看来,您是否依靠其他32位来为您清零?我见过某些不太友好的编译器(例如Intel的icpc)-这导致了运行时错误。


NULL也许不是便携式的,也不安全。可能仍有平台#define NULL 0(根据Stroustrup的FAQ:我应该使用NULL还是0?上面的问题所引用,它是第一个搜索结果之一)。至少在较旧的C ++中,0指针上下文具有特殊的概念含义。您不应该具体考虑位。还要注意的是,在不同的整数上下文(shortintlong long)“ sizeof(0)”会有所不同。我认为这个答案有点误导了。
FooF 2014年

(个人在日常生活中的C程序员,我来看望这个问题理解人们为什么要使用NULL替代(char *)0(const char *)0(struct Boo *)0(void *)0或任何要表达的意图更加清晰-而不会(在我看来)过于繁琐)
FooF

投票吧。这是在msvc2013 C编译器上发生的。在64位中,当转换为指针时,不能保证0为空指针。
pengMiao

16

如果我没记错的话,我使用的标头中对NULL的定义也有所不同。对于C,它的定义为(void *)0,对于C ++,它的定义为0。代码如下所示:

#ifndef __cplusplus
#define NULL (void*)0
#else
#define NULL 0
#endif

就我个人而言,我仍然使用NULL值来表示空指针,这使您清楚地知道您使用的是指针而不是某些整数类型。是的,在内部NULL值仍为0,但未这样表示。

另外,我不依赖于将整数自动转换为布尔值,而是明确地比较它们。

例如,更喜欢使用:

if (pointer_value != NULL || integer_value == 0)

而不是:

if (pointer_value || !integer_value)

只需说这都是在C ++ 11中进行了补救,在C ++ 11中,人们可以简单地使用nullptr代替NULLnullptr_t这就是a的类型nullptr


15

我会说历史已经讲过,那些主张使用0(零)的人是错误的(包括Bjarne Stroustrup)。赞成0的论点主要是美学和“个人偏爱”。

创建具有新nullptr类型的C ++ 11之后,一些编译器开始抱怨(使用默认参数)将0传递给具有指针参数的函数,因为0不是指针。

如果代码是使用NULL编写的,则可以通过代码库执行简单的搜索和替换,使其变为nullptr。如果您对使用选择0作为指针编写的代码感到困惑,那么更新它会很繁琐。

而且,如果您现在必须立即按照C ++ 03标准编写新代码(并且不能使用nullptr),则实际上应该只使用NULL。它将使您日后更轻松地进行更新。


11

我通常使用0。我不喜欢宏,并且不能保证您使用的某些第三方标头不会将NULL重新定义为奇怪的东西。

您可以使用Scott Meyers和其他人建议的nullptr对象,直到C ++获得nullptr关键字为止:

const // It is a const object...
class nullptr_t 
{
public:
    template<class T>
    operator T*() const // convertible to any type of null non-member pointer...
    { return 0; }

    template<class C, class T>
    operator T C::*() const   // or any type of null member pointer...
    { return 0; }

private:
    void operator&() const;  // Can't take address of nullptr

} nullptr = {};

谷歌“ nullptr”的更多信息。


9
任何将NULL定义为0以外的其他任何第三方库(或者(void*)0如果被编译为C代码)都在问问题,不应使用。
亚当·罗森菲尔德2009年

2
您是否曾经见过重新定义NULL的库?曾经吗 如果存在这样的库,那么与重新定义的NULL相比,您会遇到更大的问题,例如,您使用的库足够笨拙,无法重新定义NULL。
安迪·莱斯特

1
十多年前,我隐约记得要处理一些确实定义了NULL的第三方标头,可能是Orbix或ObjectStore。我想在浪费几天和夜晚试图让各种3rd-party标头与windows.h一起使用后,我对宏产生了病理上的仇恨。
jon-hanson 2013年

2
“不喜欢宏”是对类似于对象的#define的古怪批评。也许您是想说您不喜欢C预处理器?
安德鲁·普罗克

@Andrew -唯一的好处NULL(type *)0是可搜索,在我看来。否则,如果不是C惯用语,则似乎会造成不必要的混淆。我个人认为NULL遍及整个地方的成语值得死。NULL我认为是无用的宏。奥卡姆(Occam)的剃刀需要在这里做一些工作...
FooF 2014年

11

我曾经在机器上工作过,其中0是有效地址,而NULL被定义为特殊的八进制值。在该机器上(0!= NULL),因此代码如下

char *p;

...

if (p) { ... }

将无法按您期望的那样工作。你不得不写

if (p != NULL) { ... }

尽管我相信现在大多数编译器都将NULL定义为0,但我仍然记得那些年前的教训:NULL不一定为0。


26
您没有使用兼容的编译器。该标准说NULL 0,并且编译器应将指针上下文中的0转换为该弓的正确的NULL值。
伊万·特兰

17
是的,你是对的。这是在ANSI产生C标准之前的80年代中期。那时就没有合规性了,编译器作者可以自由地解释他们认为合适的语言。这就是为什么需要标准的原因。
mxg

9

我认为该标准可以保证NULL == 0,因此您可以执行任何操作。我更喜欢NULL,因为它记录了您的意图。


如果您有嵌套的结构,我想说的foo.bar_ptr = (Bar *) 0表达意图要比清晰得多foo.bar_ptr = NULL。这种习惯还使编译器能够为您捕获误解错误。对我来说,foo.bar_ptr = 0表达意图以及使用(NULL如果我知道那foo.bar_ptr是一个指针)。
FooF 2014年

9

使用0或NULL将具有相同的效果。

但是,这并不意味着它们都是好的编程习惯。鉴于性能没有差异,因此选择低级别感知选项而不是不可知论者/抽象主义者是一种不好的编程习惯。帮助您的代码读者理解您的思考过程

NULL,0、0.0,'\ 0',0x00和其他都转换为相同的东西,但它们在程序中是不同的逻辑实体。它们应按原样使用。NULL是一个指针,0是数量,0x0是一个其位感兴趣的值,等等。无论是否编译,您都不会将'\ 0'分配给指针。

我知道有些社区鼓励通过破坏环境合同来展示对环境的深入了解。但是,负责任的程序员要编写可维护的代码,并将此类做法排除在代码之外。


5

奇怪,没有人,包括Stroustroup都提到了这一点。虽然谈论了很多的标准和美学没有人注意到它是危险的使用0NULL的代替,例如,在变量参数列表上的架构,其中sizeof(int) != sizeof(void*)。像Stroustroup一样,0出于美学原因,我也更喜欢它,但是必须小心不要在类型可能不明确的地方使用它。


在那些危险的地方,仍可以使用0前提是你指定哪个0你的意思-例如(int *)0(char *)0(const char *)0(void *)0(unsigned long long) 0或什么的。我认为这比表示意图更明确NULL
FooF 2014年

1
当然,如果您不知道NULL代表什么。
Michael Krelin-黑客2014年

我个人发现,当我可以使用确切的类型时,不必要地将某些内容强制转换为东西有点令人反感(void *)。我故意给出了列表中(通常)64位整数的示例,因为它类似于指针的情况。此外,如果我记得较早的C ++定义NULL0准确(自从我用C ++编程已经有数年了),那么我们发现程序的正确性没有任何改善。幸运的是nullptr,较新的C ++标准提供了 关键字,因此NULL在编写较新的C ++时,我们可以摆脱这种麻烦和整个争论。
FooF 2014年

好了,这就是为什么铸造(void*)已经被抽象成NULL。而且NULL实际上在大多数时候确实很清楚地表达了意图。而且我认为您的回忆是错误的。不确定标准,但实际上我相信是这样(void*)0nullptr是的,这是一个很好的修饰词,尽管它具有相同的NULL含义-在不指定类型的情况下指定空指针。
Michael Krelin-黑客2014年

1
@FooF,在某些平台上-也许是。在我的现实中它起作用了,因此我怀疑它已被定义为指针。至于健壮性,是的,我要说的是使用nullptr带有与相同的信息NULL,仅用于表达您一开始就提到的意图。(NULL对现代gcc产量进行预处理__null,无论它是什么)。
Michael Krelin-黑客2014年

4

我尝试通过尽可能使用C ++引用来避免整个问题。而不是

void foo(const Bar* pBar) { ... }

你可能经常可以写

void foo(const Bar& bar) { ... }

当然,这并不总是有效的。但是空指针可能会被过度使用。


3

我在Stroustrup上使用它:-)因为NULL不是语言的一部分,所以我更喜欢使用0。


3

通常是个人喜好,尽管人们可能会说NULL使该对象很明显是当前不指向任何内容的指针的论点,例如

void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used

IIRC,该标准不需要NULL为0,因此使用<stddef.h>中定义的任何内容可能最适合您的编译器。

该论点的另一个方面是您应该使用逻辑比较(隐式转换为bool)还是对NULL进行显式检查,但这也取决于可读性。


3

我更喜欢使用NULL,因为它可以清楚地表明您的意图是该值表示一个指针而不是算术值。它是一个宏的事实是不幸的,但是由于它已经根深蒂固,因此几乎没有危险(除非有人真正做到了愚蠢的事情)。我确实希望它从一开始就是一个关键字,但是您该怎么办?

也就是说,将指针用作真值本身没有问题。就像NULL一样,这是一个根深蒂固的习惯用法。

C ++ 09将添加nullptr构造,我认为该构造早就该了。


1

我总是使用0。这并不是出于任何真正的深思熟虑的原因,只是因为我初次学习C ++时,我读到了建议使用0的内容,而我一直都这样做。从理论上讲,可读性可能会引起混乱,但实际上,我从未在数千个工时和数百万行代码中遇到过这样的问题。正如Stroustrup所说,在标准变为nullptr之前,这实际上只是个人审美问题。


1

有人告诉我一次...我将NULL重新定义为69。从那时起,我就不再使用它了:P

它使您的代码非常脆弱。

编辑:

并非标准中的所有内容都是完美的。宏NULL是一个实现定义的C ++空指针常量,与C NULL宏不完全兼容,除了隐藏类型的隐式转换外,它还可以用在无用且容易出错的工具中。

NULL不能作为空指针,而可以作为O / OL文字。

告诉我下一个例子不要混淆:

void foo(char *); 
void foo(int); 
foo(NULL); // calls int version instead of pointer version! 

正因为如此,在新标准中出现了std :: nullptr_t

如果您不想等待新标准并想使用nullptr,请至少使用像Meyers所建议的那样体面的标准(请参阅jon.h注释)。


5
NULL是C ++标准中定义明确的部分。让喜欢重新定义标准宏的人在您的项目中编辑代码会使您的代码“易受攻击”;使用NULL没有。
CB Bailey

1

好吧,我主张尽可能不使用0或NULL指针。

使用它们迟早会导致代码中的分段错误。以我的经验,gereral中的指针是C ++中最大的bug来源之一。

同样,它会导致整个代码中出现“ if-not-null”语句。如果您始终可以依靠有效状态,那就更好了。

几乎总是有更好的选择。


2
有保证的分段错误(并且在取消引用时在现代系统上保证0)对于调试很有用。比取消引用随机垃圾并让谁知道结果好得多。
Lightness Races in Orbit

-4

将指针设置为0并不清楚。特别是如果您使用的不是C ++语言。这包括C以及Javascript。

我最近修改了一些代码,如下所示:

virtual void DrawTo(BITMAP *buffer) =0;

首次提供纯虚拟功能。我认为这是一个神奇的jiberjash一周。当我意识到这基本上只是将函数指针设置为a时null(因为在大多数情况下,对于C ++,虚函数只是函数指针),我踢了自己。

virtual void DrawTo(BITMAP *buffer) =null;

如果不给我新的眼睛适当的间距,那会比那种夸张的混乱更少混乱。实际上,我想知道为什么C ++不像现在使用null小写的false和true那样使用小写。


通常,对于指针,我更喜欢NULl而不是0。但是'= 0;' 是在C ++中声明纯虚函数的惯用方式。我强烈建议您不要使用'= NULL;' 对于这种特殊情况。
danio 2012年

这是关于StackOverflow的最有趣的评论。您现在可能已经知道,您给出的示例是纯虚函数的语法,而不是指针。是的,@ danio是正确的,您不应将NULL用于纯虚函数。
sgowd '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.