200

In a recent code review, a contributor is trying to enforce that all NULL checks on pointers be performed in the following manner:

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

instead of

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

I agree that his way is a little more clear in the sense that it's explicitly saying "Make sure this pointer is not NULL", but I would counter that by saying that anyone who's working on this code would understand that using a pointer variable in an if statement is implicitly checking for NULL. Also I feel the second method has a smaller chance of introducing a bug of the ilk:

if (some_ptr = NULL)

which is just an absolute pain to find and debug.

Which way do you prefer and why?

Bryan Marble
  • 3,467
  • 5
  • 26
  • 29
  • 49
    Having a consistent style is sometimes more important than which style you pick. – Mark Ransom Sep 29 '10 at 21:14
  • 20
    @Mark: Consistency is __always__ the most important factor of your style. – sbi Sep 29 '10 at 21:32
  • @sbi, I'm talking about group consistency, not personal consistency. When you have to interact with other people's code, different styles can drive you batty. – Mark Ransom Sep 29 '10 at 21:53
  • @Mark: My statement includes group consistency. `:)` – sbi Sep 29 '10 at 22:01
  • 17
    "Also I feel the second method has a smaller chance of introducing a bug of the ilk: if( some_ptr = NULL )" Which is why some people use if(NULL == some_ptr), can't assign to NULL so you would get a compile error. – user122302 Sep 29 '10 at 22:21
  • 16
    most compilers these days warn about assignment in conditionals -- unfortunately too many devs ignore the compiler's warnings. – Mike Ellery Sep 29 '10 at 23:42
  • your company may also have coding guidelines for this type of choice.... – T.T.T. Sep 30 '10 at 01:48
  • 12
    I don't agree with the statement above that consistency is what matters. This has nothing to do with consistency; not the Good sort anyway. This is the area where we should let programmers express their own style. If there's a person that doesn't immediately understand either form then they should stop reading code right away and consider a career change. I'll say more than that: If there's a cosmetics consistency you can't _automatically_ enforce with a script then remove it. The cost of draining human moral is **WAY** more important than those stupid decisions people make in a meeting. – wilhelmtell Dec 10 '10 at 16:54
  • 1
    @willhelmtell: the second reason for style guides is to shut up devs discussing the details of such a test ad infinitum. *If* the place regulates it, follow the style guide. *"Express your own style"* in the way you meet deadlines and provide working code. (FWIW, for a small, tight team I would recommend *not* to regulate such a detail *unless* it leads to problems.) – peterchen Jun 03 '15 at 15:17
  • http://programmers.stackexchange.com/questions/186036/when-should-pointers-be-checked-for-null-in-c – Ciro Santilli OurBigBook.com Oct 10 '15 at 23:09
  • 1
    @Bryan One side note, accidental assignment in conditionals can often be avoided by listing the value being compared to first so that an accidental assignment cannot take place. In your example, `if ( NULL != some_ptr )`. If `if ( NULL = some_ptr )` were instead accidentally entered, the compiler would catch it. – Eric Schnipke Sep 20 '18 at 13:26
  • Related on Software Engineering: [What does it mean to do a “null check” in C or C++?](https://softwareengineering.stackexchange.com/q/151867/168744) – Stevoisiak Mar 12 '19 at 17:10
  • The `if (check_for_error) { handle_error_condition; } proceed_logic;` pattern is much better than the `if (check_for_non_error) { proceed_logic; } else { handle_error_condition; }` pattern. Especially when you have, oh, a dozen different checks, and your indentation has walked off the screen. – Eljay Apr 09 '21 at 18:36
  • More than 1500 rep from an "opinions?" question.. There should be a badge for this, and it should be gold! – Nate T Jul 31 '21 at 10:05

14 Answers14

271

In my experience, tests of the form if (ptr) or if (!ptr) are preferred. They do not depend on the definition of the symbol NULL. They do not expose the opportunity for the accidental assignment. And they are clear and succinct.

Edit: As SoapBox points out in a comment, they are compatible with C++ classes such as unique_ptr, shared_ptr, auto_ptr that are objects that act as pointers and which provide a conversion to bool to enable exactly this idiom. For these objects, an explicit comparison to NULL would have to invoke a conversion to pointer which may have other semantic side effects or be more expensive than the simple existence check that the bool conversion implies.

I have a preference for code that says what it means without unneeded text. if (ptr != NULL) has the same meaning as if (ptr) but at the cost of redundant specificity. The next logical thing is to write if ((ptr != NULL) == TRUE) and that way lies madness. The C language is clear that a boolean tested by if, while or the like has a specific meaning of non-zero value is true and zero is false. Redundancy does not make it clearer.

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
RBerteig
  • 41,948
  • 7
  • 88
  • 128
  • 28
    Additionally, they are compatible with pointer wrapper classes (shared_ptr, auto_ptr, scoped_tr, etc) which typically override operator bool (or safe_bool). – SoapBox Sep 29 '10 at 21:01
  • +1 Moreover, even Stroustrup in the 3rd edition of his book suggests to use 0 instead of NULL (even in assignments) for better type checking. – Claudio Oct 15 '14 at 12:06
  • 1
    One important thing to note: until recently, I was in favor of writing "if (ptr)" instead of "if (ptr != NULL)" or "if (ptr != nullptr)" as it is more concise so it makes the code more readable. But I just found out that comparison raises (resp.) warning/error for comparison with NULL/nullptr on recent compilers. So if you want to check a pointer, better to do "if (ptr != NULL)" instead of "if (ptr)". If you mistakenly declare ptr as an int, the first check will raise a warning/error, while the latter will compile without any warning. – Arnaud Nov 05 '14 at 09:21
  • "They do not depend on the definition of the symbol NULL". It is always a null pointer constant though. It can never evaluate to true. – Lundin Jun 30 '16 at 14:50
  • someone points here [1] that `if(!ptr)` is undefined behavior [1]: http://kukuruku.co/hub/programming/i-do-not-know-c – David 天宇 Wong Feb 14 '17 at 15:14
  • 1
    @David天宇Wong No, that is not what was meant. The example at that page is the two line sequence `int y = *x; if (!x) {...}` where the optimizer is allowed to reason that since `*x` would be undefined if `x` is NULL, it must not be NULL and hence `!x` must be FALSE. The expression `!ptr` is *not* undefined behavior. In the example, if the test were made *before* any use of `*x`, then the optimizer would be wrong to eliminate the test. – RBerteig Feb 23 '17 at 00:21
  • To make this work you have to initialize the pointer = NULL, otherwise, it makes possible to get a random initialization and crash with a bad memory access. – Carlos Oliveira Sep 27 '17 at 21:48
  • 1
    Bjarne Stroustrup (the creator of C++) agrees with this answer in his official C++ Guidelines: http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-if. The rule: **ES.87: Don’t add redundant == or != to conditions**. See details & a couple examples at the link. – Gabriel Staples Jan 10 '19 at 23:24
64

if (foo) is clear enough. Use it.

wilx
  • 17,697
  • 6
  • 59
  • 114
35

I'll start off with this: consistency is king, the decision is less important than the consistency in your code base.

In C++

NULL is defined as 0 or 0L in C++.

If you've read The C++ Programming Language Bjarne Stroustrup suggests using 0 explicitly to avoid the NULL macro when doing assignment, I'm not sure if he did the same with comparisons, it's been a while since I read the book, I think he just did if(some_ptr) without an explicit comparison but I am fuzzy on that.

The reason for this is that the NULL macro is deceptive (as nearly all macros are) it is actually 0 literal, not a unique type as the name suggests it might be. Avoiding macros is one of the general guidelines in C++. On the other hand, 0 looks like an integer and it is not when compared to or assigned to pointers. Personally I could go either way, but typically I skip the explicit comparison (though some people dislike this which is probably why you have a contributor suggesting a change anyway).

Regardless of personal feelings this is largely a choice of least evil as there isn't one right method.

This is clear and a common idiom and I prefer it, there is no chance of accidentally assigning a value during the comparison and it reads clearly:

if (some_ptr) {}

This is clear if you know that some_ptr is a pointer type, but it may also look like an integer comparison:

if (some_ptr != 0) {}

This is clear-ish, in common cases it makes sense... But it's a leaky abstraction, NULL is actually 0 literal and could end up being misused easily:

if (some_ptr != NULL) {}

C++11 has nullptr which is now the preferred method as it is explicit and accurate, just be careful about accidental assignment:

if (some_ptr != nullptr) {}

Until you are able to migrate to C++0x I would argue it's a waste of time worrying about which of these methods you use, they are all insufficient which is why nullptr was invented (along with generic programming issues which came up with perfect forwarding.) The most important thing is to maintain consistency.

In C

C is a different beast.

In C NULL can be defined as 0 or as ((void *)0), C99 allows for implementation defined null pointer constants. So it actually comes down to the implementation's definition of NULL and you will have to inspect it in your standard library.

Macros are very common and in general they are used a lot to make up for deficiencies in generic programming support in the language and other things as well. The language is much simpler and reliance on the preprocessor more common.

From this perspective I'd probably recommend using the NULL macro definition in C.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
M2tM
  • 4,415
  • 1
  • 34
  • 43
  • 1
    tl;dr and although you're right about `0` in C++, it has meaning in C: `(void *)0`. `0` is preferable to `NULL` in C++, because type errors can be a PITA. – Matt Joiner Sep 30 '10 at 04:27
  • @Matt: But `NULL` is zero anyway. – GManNickG Sep 30 '10 at 07:19
  • @GMan: Not on my system: `#define NULL ((void *)0)` from `linux/stddef.h` – Matt Joiner Sep 30 '10 at 09:49
  • @Matt: In C++. You said 0 is preferable, but `NULL` must be zero. – GManNickG Sep 30 '10 at 13:56
  • This is a good point, I neglected to mention it and I'm improving my answer by suggesting it. ANSI C can have NULL defined as ((void *)0), C++ defines NULL as 0. I haven't trawled the standard for this directly but my understanding is that in C++ NULL can be 0 or 0L. – M2tM Sep 30 '10 at 21:27
  • @GMan: Oh right. Yeah I don't see anythign wrong with using `0` in C++, as it won't cause type errors in comparisons with pointers, and `NULL` is `0` in C++ anyway (why bother to use a macro in this case). But in C, I'd definitely use `NULL`. Personally I'd use `nullptr` in C++ and define a `const class` or use the upcoming standard. – Matt Joiner Oct 01 '10 at 00:46
  • It's important to note in the context of this question I purposefully recommended two different approaches depending on the language chosen. :) – M2tM Oct 01 '10 at 00:49
20

I use if (ptr), but this is completely not worth arguing about.

I like my way because it's concise, though others say == NULL makes it easier to read and more explicit. I see where they're coming from, I just disagree the extra stuff makes it any easier. (I hate the macro, so I'm biased.) Up to you.

I disagree with your argument. If you're not getting warnings for assignments in a conditional, you need to turn your warning levels up. Simple as that. (And for the love of all that is good, don't switch them around.)

Note in C++0x, we can do if (ptr == nullptr), which to me does read nicer. (Again, I hate the macro. But nullptr is nice.) I still do if (ptr), though, just because it's what I'm used to.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • hope extra 5 years of experience changed your mind :) if C++/C had operator something like if_exist(), then it would make sense. – magulla Aug 26 '16 at 20:16
11

Frankly, I don't see why it matters. Either one is quite clear and anyone moderately experienced with C or C++ should understand both. One comment, though:

If you plan to recognize the error and not continue executing the function (i.e., you are going to throw an exception or return an error code immediately), you should make it a guard clause:

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

    // p is not null
    return 0;
}

This way, you avoid "arrow code."

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 1
    someone pointed here http://kukuruku.co/hub/programming/i-do-not-know-c that `if(!p)` is undefined behavior. It could work or not. – David 天宇 Wong Feb 14 '17 at 15:15
  • 3
    @David天宇Wong if you're talking about example #2 at that link, the `if(!p)` is not the undefined behavior, it's the line prior to it. And `if(p==NULL)` would have the exact same problem. – Mark Ransom May 04 '20 at 17:14
9

Personally I've always used if (ptr == NULL) because it makes my intent explicit, but at this point it's just a habit.

Using = in place of == will be caught by any competent compiler with the correct warning settings.

The important point is to pick a consistent style for your group and stick to it. No matter which way you go, you'll eventually get used to it, and the loss of friction when working in other people's code will be welcome.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
9

Just one more point in favor of the foo == NULL practice: If foo is, say, an int * or a bool *, then the if (foo) check can accidentally be interpreted by a reader as testing the value of the pointee, i.e. as if (*foo). The NULL comparison here is a reminder that we're talking about a pointer.

But I suppose a good naming convention makes this argument moot.

Daniel Hershcovich
  • 3,751
  • 2
  • 30
  • 35
7

The C Programming Language (K&R) would have you check for null == ptr to avoid an accidental assignment.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Derek
  • 11,715
  • 32
  • 127
  • 228
  • 25
    K&R would also ask "what's a smart pointer?" Things are changing in C++ world. – Alex Emelianov Sep 29 '10 at 21:07
  • 2
    or rather, things have changed already in C++ world" ;) – jalf Sep 29 '10 at 23:28
  • Except that things have changed 10 years too late. – Matt Joiner Sep 30 '10 at 04:29
  • 3
    Where do K&R state that (ref)? They never use this style themselves. In K&R2 chapter 2 they even recommend `if (!valid)` above `if (valid == 0`). – schot Sep 30 '10 at 07:48
  • 6
    Settle down, folks. He said k&r, not K&R. They are obviously less famous as their initials aren't even capitalized. – jmucchiello Sep 30 '10 at 17:28
  • 1
    capitalizing just slows you down – Derek Sep 30 '10 at 18:26
  • 1
    If I ask what is K&R would that expose my C++ noobness.... But somebody got to ask for everyone to learn. Bring on the flame! – Ali Akdurak Dec 29 '13 at 09:50
  • Well, it's 13 years later, but…I think both this answer and schot's response mischaracterize K&R. K&R does check for `p == NULL` or `p != NULL` frequently in the latter chapters (see e.g. chapter 6 section 4 for an example of both). The book actually says that `if (valid == 0)` and `if (!valid)` are equally good and it's a matter of preference, but furthermore it's introducing conditionals there, not pointers which come later. However, K&R always uses a `NULL` pointer to say something, such as "new node needed here" or "end of list"; I don't think they ever do defensive "`NULL` checks". – Zoë Sparks Jul 30 '23 at 01:34
5

Actually, I use both variants.

There are situations, where you first check for the validity of a pointer, and if it is NULL, you just return/exit out of a function. (I know this can lead to the discussion "should a function have only one exit point")

Most of the time, you check the pointer, then do what you want and then resolve the error case. The result can be the ugly x-times indented code with multiple if's.

dwo
  • 3,576
  • 2
  • 22
  • 39
  • 2
    I personally hate the arrow code that would result using one exit point. Why don't exit if I already know the answer? – ruslik Sep 29 '10 at 21:11
  • 1
    @ruslik: in C (or C-with-classes style C++), having multiple returns makes cleanup harder. In "real" C++ that's obviously a non-issue because your cleanup is handled by RAII, but some programmers are (for more or less valid reasons) stuck in the past, and either refuse, or aren't allowed to, rely on RAII. – jalf Sep 29 '10 at 23:27
  • @jalf in such cases a `goto` may be very handy. Also in many cases a `malloc()` that would need cleanup could be replaced by a faster `alloca()`. I know they aren't recommended, but they exist for a reason (by me, a well named label for `goto` is much cleaner than an obfuscated `if/else` tree). – ruslik Sep 29 '10 at 23:52
  • 1
    `alloca` is nonstandard and completely non-safe. If it fails, your program just blows up. There is no chance for recovery, and since the stack is relatively small, failure is likely. On failure, it's possible that you clobber the heap and introduce privilege-compromising vulnerabilities. Never use `alloca` or vlas unless you have a *tiny* bound on the size that will be allocated (and then you might as well just use a normal array). – R.. GitHub STOP HELPING ICE Sep 30 '10 at 01:02
3

If style and format are going to be part of your reviews, there should be an agreed upon style guide to measure against. If there is one, do what the style guide says. If there's not one, details like this should be left as they are written. It's a waste of time and energy, and distracts from what code reviews really ought to be uncovering. Seriously, without a style guide I would push to NOT change code like this as a matter of principle, even when it doesn't use the convention I prefer.

And not that it matters, but my personal preference is if (ptr). The meaning is more immediately obvious to me than even if (ptr == NULL).

Maybe he's trying to say that it's better to handle error conditions before the happy path? In that case I still don't agree with the reviewer. I don't know that there's an accepted convention for this, but in my opinion the most "normal" condition ought to come first in any if statement. That way I've got less digging to do to figure out what the function is all about and how it works.

The exception to this is if the error causes me to bail from the function, or I can recover from it before moving on. In those cases, I do handle the error first:

if (error_condition)
  bail_or_fix();
  return if not fixed;

// If I'm still here, I'm on the happy path

By dealing with the unusual condition up front, I can take care of it and then forget about it. But if I can't get back on the happy path by handling it up front, then it should be handled after the main case because it makes the code more understandable. In my opinion.

But if it's not in a style guide then it's just my opinion, and your opinion is just as valid. Either standardize or don't. Don't let a reviewer pseudo-standardize just because he's got an opinion.

Darryl
  • 5,907
  • 1
  • 25
  • 36
2

This is one of the fundamentals of both languages that pointers evaluate to a type and value that can be used as a control expression, bool in C++ and int in C. Just use it.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

I'm a huge fan of the fact that C/C++ doesn't check types in the boolean conditions in if, for and while statements. I always use the following:

if (ptr)

if (!ptr)

even on integers or other type that converts to bool:

while(i--)
{
    // Something to do i times
}

while(cin >> a >> b)
{
    // Do something while you've input
}

Coding in this style is more readable and clearer to me. Just my personal opinion.

Recently, while working on OKI 431 microcontroller, I've noticed that the following:

unsigned char chx;

if (chx) // ...

is more efficient than

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

because in later case the compiler has to compare the value of chx to 1. Where chx is just a true/false flag.

Donotalo
  • 12,748
  • 25
  • 83
  • 121
1

Most compilers I've used will at least warn on the if assignment without further syntax sugar, so I don't buy that argument. That said, I've used both professionally and have no preference for either. The == NULL is definitely clearer though in my opinion.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael Dorgan
  • 12,453
  • 3
  • 31
  • 61
1
  • Pointers are not booleans
  • Modern C/C++ compilers emit a warning when you write if (foo = bar) by accident.

Therefore I prefer

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

or

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

However, if I were writing a set of style guidelines I would not be putting things like this in it, I would be putting things like:

Make sure you do a null check on the pointer.

JeremyP
  • 84,577
  • 15
  • 123
  • 161
  • 4
    It's true that pointers are not booleans, but in C, if-statements don't take booleans: they take integer expressions. – Ken Sep 30 '10 at 18:06
  • 1
    @Ken: that's because C is broken in that respect. Conceptually, it's a boolean expression and (in my opinion) should be treated as such. – JeremyP Oct 01 '10 at 08:46
  • 2
    Some languages have if-statements that only test for null/not-null. Some languages have if-statements that only test an integer for sign (a 3-way test). I see no reason to consider C "broken" because they chose a different concept that you like. There's lots of things I hate about C but that just means the C program model is not the same as my mental model, not that either of us (me or C) is broken. – Ken Oct 15 '10 at 00:32
  • 1
    @Ken: A boolean is not a number or a pointer *conceptually*, Never mind which language. – JeremyP Oct 15 '10 at 08:21
  • 2
    I didn't say that a boolean was a number or pointer. I said there's no reason to insist that an if-statement should or could only take a boolean expression, and offered counterexamples, in addition to the one at hand. Lots of languages take something other than a boolean, and not just in C's "zero/nonzero" way. In computing, having an if-statement that accepts (only) a boolean is a relatively recent development. – Ken Oct 16 '10 at 02:27
  • " In computing, having an if-statement that accepts (only) a boolean is a relatively recent development." - No it isn't. Not unless you count the 60's as recent. Conceptually an if statement should take a boolean expression. Bools are not numbers or strings or pointers and converting between the two can often lead to trouble. – JeremyP Oct 17 '10 at 21:33
  • @JeremyP Mixing dynamic typing and automatic conversions is contra-indicated, sure. As long as you only have one you are fine though. – Deduplicator Oct 30 '20 at 20:49
  • just wasted a few hours because i had a `BOOL*` that I was "checking" with `if (myvar)` instead of `if (*myvar)`. I only wish there was a way to disable coercing pointers into boolean expressions, which I agree is what conceptually happens and I want to explicitly opt in to, to avoid errors like these. So upvote for this answer, which hints at this problem. – Janus Troelsen May 21 '21 at 02:42
  • @JanusTroelsen I think C's conflation of booleans and almost any scalar type is one of its biggest failings. Your problem, the problem of omitting the `!` when testing pointers and the accidental assignment problem would never be a thing if booleans were a proper separate type – JeremyP May 21 '21 at 08:02
  • @JeremyP Right. To clarify, when I said "explicitly opt in to", I mean that I want the compiler for force all users to opt in to this, *if they want*. Personally I wouldn't opt in to it, and I'd waste less time. Some users value conciseness, I value it less in this case. – Janus Troelsen May 21 '21 at 16:59