28

Religious arguments aside:

  • Option1:

    if (pointer[i] == NULL) ...
    
  • Option2:

    if (!pointer[i]) ...  
    

In C is option1 functionally equivalent to option2?

Does the later resolve quicker due to absence of a comparison ?

Ande Turner
  • 7,096
  • 19
  • 80
  • 107
  • 6
    Is it safe to assume that "pointer" is actually an array of pointers? It might make the question clearer if you remove the "[i]". – Steve Melnikoff Aug 16 '09 at 13:30
  • Sudo, you need to chill. You're yelling at jalf for an honest answer. The fact you're even asking "do these have equivalent performance" or even "are these the same" demonstrates that you need to open up your mind and not assume everything is an attack. – GManNickG Aug 17 '09 at 02:58

11 Answers11

35

I prefer the explicit style (first version). It makes it obvious that there is a pointer involved and not an integer or something else but it's just a matter of style.

From a performance point of view, it should make no difference.

Laserallan
  • 11,072
  • 10
  • 46
  • 67
31

Equivalent. It says so in the language standard. And people have the damndest religious preferences!

Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
23

I like the second, other people like the first.

Actually, I prefer a third kind to the first:

if (NULL == ptr) {
   ...
}

Because then I:

  • won't be able to miss and just type one =
  • won't miss the == NULL and mistake it for the opposite if the condition is long (multiple lines)

Functionally they are equivalent.

Even if a NULL pointer is not "0" (all zero bits), if (!ptr) compares with the NULL pointer.

The following is incorrect. It's still here because there are many comments referring to it: Do not compare a pointer with literal zero, however. It will work almost everywhere but is undefined behavior IIRC.

0x4d45
  • 704
  • 1
  • 7
  • 18
Thomas
  • 4,208
  • 2
  • 29
  • 31
  • I do not like the readability of this method. In java you might avoid a NullPointerException by using if ("hello".equals(obj.getAttr())) but in this case you have nothing in favor of decreasing the readability... – Yuval Adam Aug 16 '09 at 13:41
  • 3
    "Do not compare a pointer with literal zero, however. It will work almost everywhere but is undefined behavior IIRC." <- No that's wrong. What makes you say it's undefined behavior? – Johannes Schaub - litb Aug 16 '09 at 13:49
  • 8
    Thomas, see http://c-faq.com/null/ptrtest.html. It's perfectly safe to compare a pointer to literal 0. – Andrew Keeton Aug 16 '09 at 14:37
  • There may in fact be something valid at (void *)0 on some architectures. – dreamlax Aug 16 '09 at 21:44
  • 2
    But, NULL is #defined to be 0. So if you're comparing to NULL, you ARE comparing to 0, as far as the compiler is concerned, because the compiler processes not what you type, but the preprocessor's output, and the preprocessor with replace NULL with 0. So the fact that there may be something valid at (void *) 0 is irrelevant. The NULL will be replaced by 0 before the compiler sees it. – smcameron Aug 17 '09 at 02:52
  • 2
    @Dreamlax you are bloody wrong. You confuse the null pointer value and the null pointer constant. NULL is always valid when defined as 0. Defining it as 0xFFFF of course is never valid. NULL is a null pointer constant. As an exception `(void*)0` is too, in C. When converted to a pointer type, it becomes a null pointer value. This is when it becomes a pointer pointing to some address not equal 0x0. But that has nothing to do with comparing a pointer to 0. This does not compare to address 0. An integer isn't an address. – Johannes Schaub - litb Aug 17 '09 at 15:22
  • 3
    @dreamlax: from the C99 standard 6.3.2.3: "An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant.55) If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function." Which basically says "0 used in a pointer context **is** NULL." – Evan Teran Aug 17 '09 at 18:21
  • 1
    Also from c++ standard 4.10: (emphasis added) "A null pointer constant is an integral constant expression (5.19) rvalue of integer type that **evaluates to zero**. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of pointer to object or pointer to function type." – Evan Teran Aug 17 '09 at 18:23
  • Woah, you know that point where you're so sleepy you start dreaming but you're still awake, and somehow your understanding of the dream world makes sense in the waking world? I think I was there last night. Sorry all. – dreamlax Aug 17 '09 at 20:18
  • @smcameron: Contrary to C++, in C, `NULL` is not `0` but rather `(void*) 0` - I don't know, whether that makes an actual difference though. – MikeMB Sep 11 '15 at 19:02
19

It is often useful to assume that compiler writers have at least a minimum of intelligence. Your compiler is not written by concussed ducklings. It is written by human beings, with years of programming experience, and years spent studying compiler theory. This doesn't mean that your compiler is perfect, and always knows best, but it does mean that it is perfectly capable of handling trivial automatic optimizations.

If the two forms are equivalent, then why wouldn't the compiler just translate one into the other to ensure both are equally efficient?

If if (pointer[i] == NULL) was slower than if (!pointer[i]), wouldn't the compiler just change it into the second, more efficient form?

So no, assuming they are equivalent, they are equally efficient.

As for the first part of the question, yes, they are equivalent. The language standard actually states this explicitly somewhere -- a pointer evaluates to true if it is non-NULL, and false if it is NULL, so the two are exactly identical.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 8
    I think it does, in fact. I'm sorry if you feel offended by it. I'm not out to attack anyone, just pointing out that this should, if you stop and think about it, be completely and utterly obvious. If two trivial language construct mean the same thing, then they will be compiled the same way. If you want to talk about condescension, how are you better? You are assuming that you, a beginner, is, in about 30 seconds, able to perform better optimizations than the highly experienced developers who wrote your compiler. That seems pretty rude to me too. ;) – jalf Aug 16 '09 at 13:37
  • 8
    @jalf: If you've never had to use a compiler written by concussed *drunk* ducklings, you can't have been writing software for that long ;-) Luckily, however, natural selection has disposed of most of these horror stories - but I expect a few are still around... More importantly: compiler writers aren't gods - they're fallible like the rest of us, and buggy code still get written. Example: I've recently been dealing with a c++ compiler/rtl where measuring the length of a const string would invoke the copy constructor every time... – Roddy Aug 16 '09 at 20:45
9

Almost certainly no difference in performance. I prefer the implicit style of the second, though.

Barry Kelly
  • 41,404
  • 5
  • 117
  • 189
3

NULL should be declared in one of the standard header files as such:

#define NULL ((void*)0)

So either way, you are comparing against zero, and the compiler should optimize both the same way. Every processor has some "optimization" or opcode for comparing with zero.

Mark Rushakoff
  • 249,864
  • 45
  • 407
  • 398
1

Early optimization is bad. Micro optimization is also bad, unless you are trying to squeeze every last bit of Hz from your CPU, there is no point it doing it. As people have already shown, the compiler will optimize most of your code away anyways.

Its best to make your code as concise and readable as possible. If this is more readable

if (!ptr)

than this

if (NULL==ptr)

then use it. As long as everyone who will be reading your code agrees.

Personally I use the fully defined value (NULL==ptr) so it is clear what I am checking for. Might be longer to type, but I can easily read it. I'd think the !ptr would be easy to miss ! if reading to quickly.

0

It really depends on the compiler. I'd be surprised if most modern C compilers didn't generate virtually identical code for the specific scenario you describe.

Get your compiler to generate an assembly listing for each of those scenarios and you can answer your own question (for your particular compiler :)).

And even if they are different, the performance difference will probably be irrelevant in practical applications.

0

Turn on compiler optimization and they're basically the same

tested this on gcc 4.3.3

int main (int argc, char** argv) {
   char c = getchar();
   int x = (c == 'x');
   if(x == NULL)
      putchar('y');
   return 0;
}

vs

int main (int argc, char** argv) {
   char c = getchar();
   int x = (c == 'x');
   if(!x)
      putchar('y');
   return 0;
}


gcc -O -o test1 test1.c
gcc -O -o test2 test2.c


diff test1 test2

produced no output :)

Charles Ma
  • 47,141
  • 22
  • 87
  • 101
  • Also, passing gcc `-O0` for no optimizations indeed results in different binaries. – Mark Rushakoff Aug 16 '09 at 11:29
  • 4
    Why are people up-voting this reply? The question is about testing pointers, whereas this example tests an integer. It doesn't make sense to test an integer against NULL; in fact I suspect that you would get a compiler warning if NULL was properly defined and warnings were turned on. – Dipstick Aug 16 '09 at 12:36
-2

I did a assembly dump, and found the difference between the two versions:

@@ -11,8 +11,7 @@
pushl %ecx
subl $20, %esp
movzbl -9(%ebp), %eax
- movsbl %al,%eax
- testl %eax, %eax
+ testb %al, %al

It looks like the latter actually generates one instruction and the first generates two, but this is pretty unscientific.

This is gcc, no optimizations:

test1.c:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{ 
  char *pointer[5];

if(pointer[0] == NULL) {
  exit(1);
}

exit(0);

}

test2.c: Change pointer[0] == NULL to !pointer[0]

gcc -s test1.c, gcc -s test2.c, diff -u test1.s test2.s

ctennis
  • 207
  • 1
  • 5
  • 6
    What was your optimization level, and what was your compiler? – Mark Rushakoff Aug 16 '09 at 11:25
  • 5
    And why are you comparing `char` to `NULL`? The question was about pointers. (Perhaps I'm reading the disassembly wrong, feel free to bash me if it's so.) – avakar Aug 16 '09 at 11:31
  • 1
    Glad to see, I've read it correctly. :) Turn on optimizations, the code will most likely be same in both tests. – avakar Aug 16 '09 at 11:34
  • I changed the code to an array of pointers, but the end result is still the same. – ctennis Aug 16 '09 at 11:41
  • Also, putting -O3 in my compile line doesn't change the result (gcc 4.3). Not that it can't be optimized out, of course. – ctennis Aug 16 '09 at 11:42
  • Stratch that - optiziming to -O3 does indeed make the output the same. – ctennis Aug 16 '09 at 11:44
  • 1
    Not only it makes the output the same, using -O2 results in a simple call to `exit(1)`, the comparison is optimized out completely. Note also, that your snippet features undefined behavior (accessing an uninitialized object). – avakar Aug 16 '09 at 11:52
  • Yessir, it was just a quick hack. :) – ctennis Aug 16 '09 at 11:53
  • 9
    -1 for comparing with optimizations disabled. It is misleading and useless. – jalf Aug 16 '09 at 12:24
-2
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{   
  char pointer[5];  
  /* This is insense you are comparing a pointer to a value */   
  if(pointer[0] == NULL) {     
    exit(1);
  } 
  ...
}

=>           ...    
  movzbl    9(%ebp), %eax  # your code compares a 1 byte value to a signed 4 bytes one 
  movsbl    %al,%eax        # Will result in sign extension...
  testl  %eax, %eax      
              ...

Beware, gcc should have bumped out a warning, if not the case compile with -Wall flag on Though, you should always compile to optimized gcc code. BTW, precede your variable with volatile keyword in order to avoid gcc from ignoring it...

Always mention your compiler build version :)

ZeroCool
  • 1,490
  • 4
  • 19
  • 33
  • 1
    In the original question 'pointer' is obviously an array of pointers, not chars. –  Aug 16 '09 at 12:22