在C/C++中检查NULL指针

Bry*_*ble 142 c c++ coding-style code-formatting

在最近的代码审查中,贡献者试图强制执行NULL对指针的所有检查以下列方式执行:

int * some_ptr;
// ...
if (some_ptr == NULL)
{
    // Handle null-pointer error
}
else
{
    // Proceed
}
Run Code Online (Sandbox Code Playgroud)

代替

int * some_ptr;
// ...
if (some_ptr)
{
    // Proceed
}
else
{
    // Handle null-pointer error
}
Run Code Online (Sandbox Code Playgroud)

我同意他的方式更明确,因为它明确地说"确保这个指针不是NULL",但我会反驳说,任何正在研究这个代码的人都会明白在一个指针变量中使用指针变量if声明是隐式检查NULL.另外我觉得第二种方法引入类似错误的可能性较小:

if (some_ptr = NULL)
Run Code Online (Sandbox Code Playgroud)

这对于查找和调试来说只是一种绝对的痛苦.

您更喜欢哪种方式?为什么?

RBe*_*eig 188

根据我的经验,对表格进行测试if (ptr)或者if (!ptr)是首选.它们不依赖于符号的定义NULL.他们没有暴露意外分配的机会.它们清晰简洁.

编辑:正如SoapBox在注释中指出的那样,它们与C++类兼容,例如auto_ptr作为指针的对象,它提供转换bool以实现这个成语.对于这些对象,显式比较NULL必须调用转换为指针,这可能具有其他语义副作用,或者比bool转换所暗示的简单存在检查更昂贵.

我偏爱代码,说明没有不需要的文本意味着什么.if (ptr != NULL)具有相同的含义,if (ptr)但代价是多余的特异性.下一个合乎逻辑的事情是写作if ((ptr != NULL) == TRUE),这种方式就是疯狂.C语言清楚的是一个布尔通过测试if,while或类似的具有非零值的具体含义为真和零是假的.冗余并没有让它更清晰.

  • 另外,它们与指针包装类(shared_ptr,auto_ptr,scoped_tr等)兼容,它们通常覆盖操作符bool(或safe_bool). (23认同)
  • 我将此作为"答案"投票,仅仅是因为引用了auto_ptr添加到讨论中.在写完问题之后,很明显这实际上比其他任何东西更主观,并且可能不适合堆栈溢出"回答",因为根据情况,这些都可能是"正确的". (2认同)
  • @Bryan,我尊重这一点,如果出现"更好"的答案,请随时根据需要移动复选标记.至于主观性,是的,它是主观的.但这至少是一个主观的讨论,在提出问题时,客观问题并不总是显而易见的.这条线很模糊.主观...... ;-) (2认同)

wil*_*ilx 52

if (foo)很清楚.用它.


M2t*_*2tM 24

我将从这开始:一致性是王道,决定不如你的代码库中的一致性重要.

在C++中

在C++中,NULL被定义为0或0L.

如果您已经阅读过C++编程语言 Bjarne Stroustrup建议在执行赋值时0明确使用NULL宏来避免宏,我不确定他是否对比较做了同样的事情,自从我读这本书以来已经有一段时间了,我想他只是做了if(some_ptr)没有明确的比较,但我对此很模糊.

这样做的原因是NULL宏具有欺骗性(几乎所有的宏都是),它实际上是0文字的,而不是名称所暗示的唯一类型.避免使用宏是C++中的一般指导原则之一.另一方面,0看起来像一个整数,并且在与指针比较或分配时不会.我个人可以去任何一种方式,但通常我会跳过明确的比较(尽管有些人不喜欢这个,这可能就是为什么你有一个贡献者提出改变的原因).

无论个人感受如何,这都是最不邪恶的选择,因为没有一种正确的方法.

这很清楚,也是一个常见的习惯用语,我更喜欢它,在比较期间没有意外分配值的可能性,它清楚地显示:

if(some_ptr){;}
Run Code Online (Sandbox Code Playgroud)

如果你知道这some_ptr是一个指针类型,这很明显,但它也可能看起来像一个整数比较:

if(some_ptr != 0){;}
Run Code Online (Sandbox Code Playgroud)

这是明确的,在通常情况下它是有道理的......但它是一个漏洞的抽象,NULL实际上是0文字的,最终可能容易被误用:

if(some_ptr != NULL){;}
Run Code Online (Sandbox Code Playgroud)

C++ 0x有nullptr,它现在是首选方法,因为它是明确和准确的,只要注意意外分配:

if(some_ptr != nullptr){;}
Run Code Online (Sandbox Code Playgroud)

在你能够迁移到C++ 0x之前,我认为浪费时间来担心你使用哪种方法,它们都是不足以解释为什么会发明nullptr(以及提出完美转发的通用编程问题) .)最重要的是保持一致性.

在C.

C是一个不同的野兽.

在C NULL中可以定义为0或((void*)0),C99允许实现定义的空指针常量.所以它实际上归结为实现的NULL定义,你必须在标准库中检查它.

宏是非常常见的,并且通常它们被用于弥补语言和其他东西中的泛型编程支持的不足.语言更简单,对预处理器的依赖更为常见.

从这个角度来看,我可能建议NULL在C中使用宏定义.

  • +1:一致性是王道. (2认同)

GMa*_*ckG 19

我用if (ptr),但这完全不值得争论.

我喜欢自己的方式,因为它很简洁,尽管其他人说它== NULL更容易阅读和更明确.我看到他们来自哪里,我只是不同意额外的东西让它变得更容易.(我讨厌宏观,所以我有偏见.)由你决定.

我不同意你的论点.如果您没有收到有条件的分配警告,则需要提高警告级别.就那么简单.(为了所有善良的爱,不要转换它们.)

注意在C++ 0x中,我们可以做到if (ptr == nullptr),对我来说确实读得更好.(再次,我讨厌宏.但是nullptr很好.)我仍然这样做if (ptr),只是因为它是我习惯的.


Jam*_*lis 9

坦率地说,我不明白为什么这很重要.任何一个都很清楚,任何适度使用C或C++的人都应该理解这两者.但有一条评论:

如果您计划识别错误而不继续执行该函数(即,您将抛出异常或立即返回错误代码),您应该将其作为保护条款:

int f(void* p)
{
    if (!p) { return -1; }

    // p is not null
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这样,您就可以避免使用"箭头代码".

  • @David天宇Wong,如果您在该链接中谈论示例#2,则“if(!p)”不是未定义的行为,而是它之前的行。而 `if(p==NULL)` 也会有完全相同的问题。 (2认同)

Mar*_*som 7

就个人而言,我一直都习惯if (ptr == NULL)因为它使我的意图明确,但在这一点上,这只是一种习惯.

任何有能力的编译器都会使用正确的警告设置来=代替使用==.

重要的是为您的团队选择一致的风格并坚持下去.无论你走哪条路,你最终会习惯它,并且欢迎在使用其他人的代码时失去摩擦力.


Dan*_*ich 7

还有一点支持这种foo == NULL做法:如果foo是,比如,a int *或a bool *,那么if (foo)检查可能会被读者意外地解释为测试指针对象的值,即为if (*foo).NULL这里的比较提醒我们,我们正在谈论一个指针.

但我认为一个好的命名约定使这个论点没有实际意义.

  • +1好点,你的提醒.我们倾向于忘记人类失败. (4认同)

Der*_*rek 6

C 编程语言(K&R) 会让您检查 null == ptr 以避免意外分配。

  • K&R 还会问“什么是智能指针?” C++ 世界中的事情正在发生变化。 (24认同)
  • 安顿下来,伙计们。他说的是k&r,不是k&r。他们显然不那么出名,因为他们的首字母甚至没有大写。 (6认同)
  • K&R 在哪里声明(参考)?他们自己从不使用这种风格。在 K&R2 第 2 章中,他们甚至建议在 `if (valid == 0`) 之上使用 `if (!valid)`。 (3认同)
  • 或者更确切地说,C++ 世界中的事情已经发生了变化”;) (2认同)

dwo*_*dwo 5

实际上,我使用这两种变体。

在某些情况下,您首先检查指针的有效性,如果它为 NULL,则只需返回/退出函数。(我知道这可能会引发“函数是否应该只有一个退出点”的讨论)

大多数时候,您检查指针,然后执行您想要的操作,然后解决错误情况。结果可能是带有多个 if 的丑陋的 x 次缩进代码。

  • 我个人讨厌使用一个退出点产生的箭头代码。如果我已经知道答案为什么不退出? (2认同)