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

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

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

代替

 int * some_ptr; // ... if (some_ptr) { // Proceed } else { // Handle null-pointer error } 

我同意他的方式更清楚些,因为它明确地说“确保这个指针不是NULL”,但我会反驳说,任何在这个代码上工作的人都会明白,在if语句隐式检查NULL 。 另外我觉得第二种方法有更小的机会引入此类错误:

 if (some_ptr = NULL) 

这只是一个绝对的痛苦find和debugging。

你喜欢哪种方式,为什么?

根据我的经验,表单if (ptr)if (!ptr)是首选。 它们不依赖于符号NULL的定义。 他们没有暴露意外分配的机会。 他们是清晰和简洁的。

编辑: SoapBox在评论中指出,它们与C ++类(如auto_ptr兼容,它们是充当指针的对象,它们提供了一个转换为bool来准确地启用这个习惯用法。 对于这些对象,与NULL的显式比较将不得不调用对指针的转换,其可能具有其他语义副作用或比bool转换暗示的简单存在检查更昂贵。

我有一个偏好的代码,说什么意思,没有不必要的文字。 if (ptr != NULL)if (ptr) if (ptr != NULL)具有相同的含义,但代价是冗余特异性。 下一个合乎逻辑的事情就是写if ((ptr != NULL) == TRUE) ,那就是疯狂。 C语言很清楚,通过ifwhile等testing的布尔值具有非零值的特定含义为true,零为false。 冗余并没有使它更清晰。

if (foo)足够清楚。 用它。

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

我喜欢我的方式,因为它是简洁的,虽然别人说== NULL使得它更容易阅读和更明确。 我看到他们来自哪里,我只是不同意多余的东西,这使得它更容易。 (我讨厌macros,所以我很偏向)。

我不同意你的观点。 如果您没有在条件中获得任务警告,则需要将警告级别提高。 就那么简单。 (为了一切美好的爱,不要把它们切换。)

注意在C ++ 0x中,我们可以做if (ptr == nullptr) ,这对我来说读起来更好。 (再次,我讨厌这个macros,但是nullptr是很好的。)但是, if (ptr) ,我仍然这样做,只是因为这是我习惯的。

我将从此开始:一致性是王道,决定并不比代码库中的一致性重要。

在C ++中

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

如果你已经阅读过C ++编程语言 Bjarne Stroustrupbuild议在做任务时明确使用0来避免NULLmacros ,但我不确定他是否在比较时也是这样,从我读这本书以来已经有一段时间了,我想他只是if(some_ptr)没有明确的比较,但我很模糊。

原因是NULLmacros是欺骗性的(几乎所有的macros),它实际上是0字面的,而不是名字所暗示的唯一types。 避免macros是C ++中的一般准则之一。 另一方面, 0看起来像一个整数,它不是比较或分配给指针。 就个人而言,我可以去任何一种方式,但通常我会略过明确的比较(尽pipe有些人不喜欢这可能是为什么你有一个贡献者build议改变)。

不pipe个人的感受如何,这在很大程度上是一个最不起眼的select,因为没有一个正确的方法。

这是一个很明显的习惯用法,我更喜欢它,在比较过程中没有意外分配一个值的机会,

 if(some_ptr){;} 

如果你知道some_ptr是一个指针types,那么这很清楚,但是它也可能看起来像一个整数比较:

 if(some_ptr != 0){;} 

这是清楚的,在一般情况下它是有道理的…但是这是一个抽象的漏洞, NULL实际上是0字面值,并可能最终被误用:

 if(some_ptr != NULL){;} 

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

 if(some_ptr != nullptr){;} 

直到你能够迁移到C ++ 0x为止,我认为这是浪费时间,担心你使用的这些方法中的哪一个,它们都是不够的,这就是为什么nullptr被发明(伴随着通用编程问题,提出了完美的转发)最重要的是保持一致性。

在C

C是一个不同的野兽。

在C NULL中可以定义为0或者((void *)0),C99允许实现定义空指针常量。 所以它实际上归结为NULL的实现定义,您将不得不在标准库中检查它。

macros是非常常见的,一般来说,它们被用来弥补语言和其他方面对通用编程支持的不足。 语言要简单得多,而且对预处理器的依赖更为普遍。

从这个angular度来看,我可能会build议在C中使用NULLmacros定义

坦率地说,我不明白为什么它很重要。 任何一个都很清楚,任何有C或C ++经验的人都应该理解这两者。 但有一点意见:

如果您打算识别错误并且不继续执行该函数(即,您将立即抛出exception或返回错误代码),则应该使其成为警戒条款:

 int f(void* p) { if (!p) { return -1; } // p is not null return 0; } 

这样,你避免了“箭头代码”。

其实,我使用这两个变种。

有些情况下,你首先检查一个指针的有效性,如果它是NULL,你只是返回/退出一个函数。 (我知道这可能导致讨论“如果一个函数只有一个退出点”)

大多数时候,你检查指针,然后做你想要的,然后解决错误的情况下。 结果可能是带有多个if的难看的x次缩进代码。

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

使用=代替==将被任何主pipe编译器捕获,并具有正确的警告设置。

重要的一点是为你的团队select一致的风格并坚持下去。 无论你走哪条路,你最终都会习惯,而且在其他人的代码中工作时会失去摩擦。

如果风格和格式将成为您的评论的一部分,应该有一个商定的风格指南来衡量。 如果有的话,请按照风格指南的说法进行操作。 如果没有一个,像这样的细节应该留下,因为他们写。 这是浪费时间和精力,并分散了代码评论真正应该发现的。 严重的是,没有一个风格指南,我会推动不要改变这样的代码作为一个原则问题,即使它不使用我喜欢的惯例。

而不是那么重要,但是我的个人偏好是if (ptr) 。 即使if (ptr == NULL)这个意思对我来说也是比较明显的。

也许他试图说,在快乐之路之前处理错误情况会更好? 在这种情况下,我仍然不同意审稿人。 我不知道这是否有一个公认的惯例,但在我看来,任何声明都应该是最“正常”的条件。 这样一来,我就没有多less工夫去弄清楚这个function是什么,它是如何工作的。

这是一个例外,如果错误导致我从函数中保释,或者我可以在继续之前从中恢复。 在这些情况下,我首先处理错误:

 if (error_condition) bail_or_fix(); return if not fixed; // If I'm still here, I'm on the happy path 

通过处理不寻常的情况,我可以照顾它,然后忘掉它。 但是,如果我不能通过前面的处理来回到快乐的道路上,那么它应该在主要情况之后处理因为它使得代码更容易理解。 我的想法是。

但是,如果它不是一个风格指南,那么这只是我的意见,你的意见是一样的有效。 要么标准化,要么没有。 不要让审稿人因为他有意见而伪造标准化。

只是有一点赞成foo == NULL做法:如果foo是一个int *或一个bool * ,那么if (foo)检查可能被读者意外地解释为testing指针的值,即如if (*foo) 。 这里的NULL比较提醒我们正在讨论一个指针。

但是我认为一个好的命名约定使得这个论点没有意义。

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

我觉得这很清楚:

 if( !some_ptr ) { // handle null-pointer error } else { // proceed } 

阅读它为“如果没有指针…”

另外它简洁,没有意外分配的危险。

这是两种语言的基本原理之一,指针评估的types和值可以用作控制expression式,C ++中的bool和C中的int 。只需使用它即可。

我对C / C ++不检查ifforwhile语句中的布尔条件中的types的事实非常感兴趣。 我总是使用以下内容:

 if (ptr) if (!ptr) 

即使在整数或其他types转换为布尔:

 while(i--) { // Something to do i times } while(cin >> a >> b) { // Do something while you've input } 

这种风格的编码对我来说更具可读性和更清晰。 只是我个人的意见。

最近,在使用OKI 431微控制器时,我注意到了以下几点:

 unsigned char chx; if (chx) // ... 

比…更有效率

 if (chx == 1) // ... 

因为在稍后的情况下,编译器必须将chx的值与1进行比较。其中,chx只是一个真/假标志。

我用过的大多数编译器至less会在没有进一步的语法糖的情况下对这个赋值语句提出警告,所以我不会买这个说法。 也就是说,我既专业地使用,也没有偏好。 == NULL在我看来是绝对清晰的。

  • 指针不是布尔值
  • 现代的C / C ++编译器会在你写if (foo = bar)的时候发出警告。

所以我比较喜欢

 if (foo == NULL) { // null case } else { // non null case } 

要么

 if (foo != NULL) { // non null case } else { // null case } 

但是,如果我正在编写一套风格指南,我不会把这样的东西放在里面,我会把这样的东西放在:

确保你对指针进行了空检查。