对C ++中的指针使用NULL或0(零)吗?

时间:2008-10-07 02:15:05

标签: c++ null

在C ++的早期,当它被用螺栓固定在C之上时,你不能使用NULL,因为它被定义为(void*)0。您无法为除void*以外的任何指针指定NULL,这使得它变得毫无用处。在那些日子里,人们接受了你使用0(零)作为空指针。

到今天,我继续使用零作为空指针,但我周围的人坚持使用NULL。我个人认为给现有值命名(NULL)没有任何好处 - 因为我也喜欢将指针测试为真值:

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

然后使用零更有意义(如果您使用NULL,则无法在逻辑上使用p && !q - 您需要明确地与NULL进行比较,除非您假设{{1} }为零,在这种情况下为什么要使用NULL)。

有没有客观的理由偏好零而不是NULL(反之亦然),或者只是个人偏好?

编辑:我应该添加(并且最初要说)使用RAII和异常,我很少使用零/空指针,但有时你确实需要它们。

21 个答案:

答案 0 :(得分:171)

这是Stroustrup对此的看法:C++ Style and Technique FAQ

  

在C ++中,NULL的定义为0,因此只有美学差异。我更喜欢避免宏,所以我使用0. NULL的另一个问题是人们有时会错误地认为它与0不同和/或不是整数。在预标准代码中,NULL有时被定义为不适合的东西,因此必须/必须避免。这些日子不太常见。

     

如果必须命名空指针,请将其命名为nullptr;这就是它在C ++ 11中所称的内容。然后,nullptr将成为关键字。

那就是说,不要让小东西流汗。

答案 1 :(得分:119)

有一些论点(其中一个是相对较新的),我认为这与Bjarne的立场相矛盾。

  1. 意图文档
  2. 使用NULL可以搜索它的使用情况,并且还强调开发人员想要使用NULL指针,无论编译器是否正在解释它如NULL或不。{/ p>

    1. 指针超载和'int'相对较少
    2. 每个人引用的例子是:

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

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

      1. 分析工具今天可以提供帮助!
      2. 即使没有C ++ 0x,现在还有一些工具可以验证NULL是否用于指针,而0用于整数类型。

        1. C++ 11将使用新的std::nullptr_t类型。
        2. 这是该表的最新参数。 0NULL的问题正在为C ++ 0x主动解决,并且您可以保证对于提供NULL的每个实现,他们将要做的第一件事是:

          #define NULL  nullptr
          

          对于那些使用NULL而不是0的人来说,改变将是类型安全的改进,很少或没有努力 - 如果有的话,它也可能会捕获他们使用过的一些错误NULL 0。对于今天使用0的任何人......呃......希望他们对正则表达式有很好的了解......

答案 2 :(得分:44)

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

答案 3 :(得分:35)

我总是使用:

  • NULL指针
  • '\0' for chars
  • 0.0用于花车和双打

其中0表示可以。这是信号意图的问题。那就是说,我不是肛门。

答案 4 :(得分:34)

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

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

static const int nullptr = 0;

现在,当符合C ++ 0x的编译器到达时,我所要做的就是删除该行。 这样做的一个很好的好处是Visual Studio已经将nullptr识别为关键字并对其进行适当的突出显示。

答案 5 :(得分: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)并不那么优雅 - 这导致了运行时错误。

答案 6 :(得分: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中得到了解决,人们可以简单地使用nullptr而不是NULL,还可以nullptr_t使用{{1}的类型}}

答案 7 :(得分:14)

我会说历史已经说过了,并且赞成使用0(零)的人是错的(包括Bjarne Stroustrup)。支持0的论据主要是美学和“个人偏好”。

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

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

如果你现在必须编写新的代码到C ++ 03标准(并且不能使用nullptr),你真的应该使用NULL。这将使您将来更容易更新。

答案 8 :(得分: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 = {};

Google“nullptr”了解更多信息。

答案 9 :(得分:11)

我曾经在一台机器上工作,其中0是有效地址,NULL被定义为一个特殊的八进制值。在那台机器上(0!= NULL),所以代码如

char *p;

...

if (p) { ... }

无法按预期工作。你必须写

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

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

答案 10 :(得分:9)

使用0或NULL将产生相同的效果。

但是,这并不意味着它们都是良好的编程习惯。鉴于性能没有差异,在不可知/抽象替代方案上选择低级别感知选项是一种糟糕的编程习惯。 帮助您的代码读者理解您的思维过程

NULL,0,0.0,'\ 0',0x00和whatelse都转换为相同的东西,但是程序中的逻辑实体不同。它们应该这样使用。 NULL是一个指针,0是数量,0x0是一个有趣的位等等。无论是否编译,都不会将“\ 0”指定给指针。

我知道有些社区鼓励通过破坏环境合同来展示对环境的深入了解。但是,负责任的程序员会制作可维护的代码,并将这些实践排除在代码之外。

答案 11 :(得分:9)

我认为标准保证NULL == 0,所以你可以做到。我更喜欢NULL,因为它记录了你的意图。

答案 12 :(得分:5)

奇怪,没有人,包括Stroustroup提到过。在谈论标准和美学时,没有人注意到在0中使用NULL代替危险,例如,在sizeof(int) != sizeof(void*)的架构中使用0 { {1}}。和Stroustroup一样,出于审美原因,我更喜欢{{1}},但是必须注意不要在类型可能不明确的地方使用它。

答案 13 :(得分:4)

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

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

你可能经常能够写

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

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

答案 14 :(得分:3)

我更喜欢使用NULL,因为它清楚表明你的意图是值表示指针而不是算术值。事实上,这是一个宏观是不幸的,但由于它是如此广泛根深蒂固,所以没有什么危险(除非有人做了一些真正的蠢事)。我希望它从一开始就是一个关键词,但你能做什么?

那就是说,我将指针用作真值本身没有问题。就像NULL一样,它是一个根深蒂固的习语。

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

答案 15 :(得分:3)

主要是个人偏好,尽管有人可以提出NULL的论点,很明显该对象是一个当前没有指向任何东西的指针,例如。

void *ptr = &something;
/* lots o' code */
ptr = NULL; // more obvious that it's a pointer and not being used
IIRC,该标准不要求NULL为0,因此使用&lt; stddef.h&gt;中定义的任何内容。可能最适合您的编译器。

论证的另一个方面是你是否应该使用逻辑比较(隐式强制转换为bool)或明确检查NULL,但这也归结为可读性。

答案 16 :(得分:3)

我和Stroustrup在这一个:-) 由于NULL不是语言的一部分,我更喜欢使用0。

答案 17 :(得分:1)

我总是使用0.不是因为任何真正考虑过的原因,只是因为当我第一次学习C ++时,我读了一些推荐使用0的东西,而我总是那样做。从理论上讲,可读性可能存在混淆问题,但在实践中,我从未在数千个工时和数百万行代码中遇到过这样的问题。正如Stroustrup所说,在标准成为nullptr之前,这只是个人审美问题。

答案 18 :(得分: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评论)。

答案 19 :(得分:1)

嗯,我认为尽可能不使用0或NULL指针。

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

此外,它会导致代码中出现“if-not-null”语句。如果你可以完全依赖有效状态,那就更好了。

几乎总会有更好的选择。

答案 20 :(得分:-4)

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

我最近使用了一些代码:

virtual void DrawTo(BITMAP *buffer) =0;

第一次用于纯虚函数。我认为这是一个神奇的jiberjash一个星期。当我意识到它基本上是将函数指针设置为null时(因为虚函数在大多数情况下只是C ++的函数指针)我自己踢了。

virtual void DrawTo(BITMAP *buffer) =null;

如果没有与我的新眼睛适当的间隔,那么会比没有那种混乱更令人困惑。实际上,我想知道为什么C ++不使用小写null,就像现在使用小写的false和true一样。