11

Is the following code safe if a null pointer is passed in?

if(ptr && *ptr == value)
{
   //do something
}

Does the order of the checks matter? Does it work if I change it to this?

if(*ptr == value && ptr)
{
   //do something
}
Elliot Hatch
  • 1,038
  • 1
  • 12
  • 26

4 Answers4

20

The former is correct and safe, the latter is not.

The built-in && operator has short-circuit semantics, meaning that the second argument is evaluated if and only if the first one is true.

(This is not the case for overloaded operators.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Thanks for the undefined behaviour pointer - I've been a practitioner for a long time and often had a lack of interest in the detail - something I am trying to remedy now. – Caribou Dec 08 '12 at 22:59
3

Yes (1) is safe because of short circuiting. This is the way that the logical statement is evaluated. If the first part of the && statement is false then the whole statement can never be true so it will not try to evaluate the second part.

and (2) is unsafe because it dereferences the null pointer first, which is undefined behaviour.

edit:

in reference to KerrekSBs comment below: Why dereferencing a null pointer is undefined behaviour?

From the first answer in that post:

Defining consistent behavior for dereferencing a NULL pointer would require the compiler to check for NULL pointers before each dereference on most CPU architectures. This is an unacceptable burdern for a language that is designed for speed.

Also there is (was historically) hardware where the memory pointed to by NULL (not always 0) is in fact addressable within the program and can be dereferenced. So because of a lack of consensus and consistency it was decided that dereferencing a null pointer would be "undefined behaviour"

Community
  • 1
  • 1
Caribou
  • 2,070
  • 13
  • 29
  • 1
    (2) is not just "unsafe"; it's outright *undefined behaviour*. – Kerrek SB Dec 08 '12 at 22:37
  • @KerrekSB: You might as well elaborate on the difference since you brought it up. – user541686 Dec 08 '12 at 22:47
  • @Mehrdad: Well, `gets` is unsafe because you can't know the required buffer length, and `printf` is unsafe because you can pass wrongly typed arguments. "Unsafe" means that the correctness of the code cannot be statically verified. That's quite different to dereferencing a null pointer, which is just always bad. – Kerrek SB Dec 08 '12 at 23:00
  • 3
    Physically, null pointer is not necessarily tied to address `0`. It can be represented by any platform-specific address. – AnT stands with Russia Dec 08 '12 at 23:05
  • @KerrekSB: Well it isn't technically "always" bad, right? The implementation may very well decide that it's a valid operation for that particular system, in which case it would be perfectly safe... it's not like the standard ***requires*** it to be undefined behavior. So I don't really see a notable difference between "unsafe" and "undefined" (though it's certainly unverifiable as you mentioned). – user541686 Dec 08 '12 at 23:12
1

If the pointer is invalid (or rather NULL as pointed out), in the first version short-circuiting will prevent the evaluation of *ptr == value, so the first one is safe.

The second one will always access *ptr, whether it is valid or not.

  • 3
    `if (ptr)` cannot check for *invalid* pointers. It can only check for *null* pointers. – Kerrek SB Dec 08 '12 at 22:36
  • 1
    @KerrekSB I was working under the assumption that "invalid" referred to "`NULL`" here. Other than that, it is rather difficult to detect pointers to nowhere in specific in C++, as you must know. –  Dec 08 '12 at 22:37
  • Depending on how you read the standard, the null pointer may actually be a perfectly *valid* pointer. I prefer to reserve the term "invalid pointer" for something like the address of a no longer existing object or the result of illegal pointer arithmetic. The standard has a section on "Safely-derived pointers" (3.7.4.3) for that. – Kerrek SB Dec 08 '12 at 22:42
1

In addition to all the others answers about ptr && *ptr == value being valid, but not the other way round, the notion of valid pointer may have different meaning.

The ptr could be an uninitialized variable, or could be obtained (e.g. by a cast from some random intptr_t integer) in such a way that it does not point anywhere (e.g. a dangling pointer) but is not null.

In that case, neither order of testing works.

Some pointers can be invalid and be non-null (then testing ptr && *ptr == value is undefined behavior). There is no portable way to test them. (but you could use operating-system or processor specific tricks).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547