15

If you have an object like NSString *someString, what is the difference, if any, between

if (!someString)

vs

if (someString == nil)

Thanks!

sergio
  • 68,819
  • 11
  • 102
  • 123
Crystal
  • 28,460
  • 62
  • 219
  • 393

6 Answers6

23

The first syntax you use:

 if (!someString)

exploits a sort of "ambiguity" of C deriving from the fact that the original standard of C lacked a proper boolean type. Therefore, any integer value equalling 0 was interpreted as "false", and any integer value different from "0" was taken as "true". The meaning of ! is therefore defined based on this convention and current versions of the C standard have kept the original definition for compatibility.

In your specific case, someString is a pointer, so it is first converted to an integer, then ! someString is interpreted as a bool value of true when someString points at the location 0x000000, otherwise it evals to "true".

This is fine in most conditions (I would say always), but in theory, NULL/nil could be different from 0x000000 under certain compilers, so (in very theory) it would be better to use the second syntax, which is more explicit:

 if (someString == nil)

It is anyway more readable and since someString is not an integer (rather a pointer), IMO, better practice in general.

EDIT: about the definition of NULL...

Whether the C standard defines NULL to be 0 is an interesting topic for me...

According to C99 standard, section 7.17, "Common definitions ":

NULL [which] expands to an implementation-defined null pointer constant;

So, NULL is defined in stddef.h to an implementation-defined null pointer constant... The same document on page 47 states:

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.

So, the null pointer constant (which is (void*)0) can be converted to a null pointer and this is guaranteed to compare unequal to a pointer to any object or function.

So, I think that basically it depends on whether the implementation decides that the result of converting a null pointer constant to a null pointer produces a pointer which converted back to an integer gives 0. It is not clear that a null pointer interpreted as an integer equals 0.

I would say that the standard really try and enforce the null pointer being 0, but leaves the door open to systems where the null pointer was not 0.

Community
  • 1
  • 1
sergio
  • 68,819
  • 11
  • 102
  • 123
  • 2
    I gave +1, but there is no ambiguity in C on the idea of a boolean value - it simply does not exist. – Perception Jul 21 '11 at 20:31
  • 3
    NULL is defined to be `0` or `(void *)0` by the C standard. Even if the underlying bit-pattern used for the null pointer value is different, it does not change the fact that the null pointer constant in C is represented as `0` or `(void *)0`. – dreamlax Jul 21 '11 at 20:34
  • @Perception: It does in C99. Check out `_Bool`. – JAB Jul 21 '11 at 20:48
  • @Perception: thanks, I improved my wording... – sergio Jul 21 '11 at 21:00
  • @dreamlax: thanks also for your comment, it prompted me to read about the NULL topic a bit; it all comes down to whether `(void*)0` is the same as the *null pointer* or not, and it seems to me that it is not, since the standard calls it null pointer constant and speaks about *conversion* to null pointer... – sergio Jul 21 '11 at 21:03
  • @JAB - I guess I should have specified the version of C on which Objective-C was originally built as opposed to the new C99. But seeing as c99 still needs to be built in LLVM with a switch, is it fair to consider it the default behaviour of Objective-C? – Perception Jul 21 '11 at 21:09
  • @Perception: It's more a matter of recognizing that the behavior can exist rather than considering it the default. – JAB Jul 21 '11 at 21:16
  • @JAB - Yes it does exist. The real irony though is that _Bool is for all intents, just an unsigned int capable of storing 0 or 1! stdbool.h has largely been designed for backwards compatibility and all conditional logic actually remains unchanged, even in c99. – Perception Jul 21 '11 at 21:25
  • It's about `nil` here, not about `NULL`, and `nil` is defined as `(id)0`! –  Aug 10 '11 at 15:04
9

For most pointers, they're equivalent, though most coders I know prefer the former as it's more concise.

For weakly linked symbols, the former resolves the symbol (and will cause a crash if it's missing) while an explicit comparison against nil or NULL will not.

Jonathan Grynspan
  • 43,286
  • 8
  • 74
  • 104
3

The bang, exclamation, ! prefix operator in C is a logical not. At least, its a version of it. If you looked at a typical logical not truth table you would see something like this:

Input       Result
  1            0
  0            1

However in C the logical not operator does something more like this:

Input       Result
non-zero      0
   0          1

So when you consider that both NULL and nil in Objective-C evaluate to 0, you know that the logical not operator applied to them will result in a 1.

Now, consider the equality == operator. It compares the value of two items and returns 1 if they are equal, 0 if they are not. If you mapped its results to a truth table then it would look exactly like the results for logical not.

In C and Objective-C programs, conditionality is actually determined by int's, as opposed to real booleans. This is because there is no such thing as a boolean data type in C. So writing something like this works perfectly fine in C:

if(5) printf("hello\n"); // prints hello

and in addition

if(2029) printf("hello\n"); // also prints hello

Basically, any non-zero int will evaluate as 'true' in C. You combine that with the truth tables for logical negation and equality, and you quickly realize that:

(! someString) and (someString == nil)

are for all intents identical!

So the next logical question is, why prefer one form over another? From a pure C view-point it would be mostly a point of style, but most (good) developers would choose the equality test for a number of reasons:

  1. It's closer to what you are trying to express in code. You are trying to check if the someString variable is nil.
  2. It's more portable. Languages like Java have a real boolean type. You cannot use bang notation on their variables or their NULL definition. Using equality where its needed makes it easier to port C to such languages later on.
  3. Apple may change the definition of nil. Ok, no they won't! But it never hurts to be safe!
Perception
  • 79,279
  • 19
  • 185
  • 195
  • "any non-zero int will evaluate as 'true' in C" Could you, please, be more specific explaining how are logical expressions evaluated to (int)0 or (int)1? Say, [if(pointer)...], pointer is at first converted to int [(int)pointer] and then [if((int)pointer != 0)] it evaluates to (int)1. Could it be so that pointer is 64bit while int is 32bit and, when it's casted to int, higher bytes are truncated (if(0xffffffff00000000) => if((int)0x00000000))? I'll be glad to see where I could be wrong. – MANIAK_dobrii Jul 10 '14 at 13:36
2

In your case it means the same thing. Any pointer that does not point to nil will return YES (true).

Normally the exclamation mark operator negates a BOOL value.

Dmitry S.
  • 8,373
  • 2
  • 39
  • 49
2

If you mean to test the condition "foo is nil" you should say that: foo == nil.

If you mean to test a boolean value for falsehood, !foo is okay, but personally I think that a skinny little exclamation point is easy to miss, so I prefer foo == NO.

Writing good code is about clearly conveying your intention not just to the compiler, but to the next programmer that comes along (possibly a future you). In both cases, the more explicit you can be about what you're trying to do, the better.

All that aside, ! and ==nil have the same effect in all the cases I can think of.

Caleb
  • 124,013
  • 19
  • 183
  • 272
0

! is a negation operator. If your object isn't allocated, you will reach the same result from a truth table as you would with an == nil operation.

But, ! is usually more used for boolean operations.

if(!isFalse) {
     //if isFalse == NO, then this operation evaluates to YES (true)
     [self doStuff];
}

When you use ! on an object like !something it just checks to see if the pointer is pointing to nil, if it doesn't, it returns true, and the if statement fires.

ColdLogic
  • 7,206
  • 1
  • 28
  • 46