0

Being a moderate time coder, I saw things like the following many times:

if (a != nullptr && a->data != nullptr) {
  // do something good with a and a->data
}

However, I never really referred to documentation regarding such a simple case.


To my mind, the following is happening:

  • First, a != nullptr is checked
  • Second, a->data != nullptr is checked

The thing now here is: if a != nullptr returns false and we have a conjunction via && then there's no point to check the second statement a->data != nullptr being true or false:

since the first statement results in a false expression, the code inside the if-statement will not be executed, we don't need to check the second expression


But recently I have returned to the question yet again:

Is if safe to have a construction like this: if (a != nullptr && a->data != nullptr) {}? Can it happen so that both* statements are checked and what I get is the nullptr dereference?

*- no multi-threading involved


What I'm asking is: is the above situation has a well defined behavior (and where can I find documentation for that) or this is generally UB that must be avoided? Is the behavior any different depending on the operating system (say, Windows vs. Linux-based)?

andrgolubev
  • 825
  • 6
  • 21

1 Answers1

2

This is safe

It's actually one of the few cases in which C++ guarantees the evaluation order; first the left-side is evaluated and then (and only if the left side is true) the second side is evaluated.

6502
  • 112,025
  • 15
  • 165
  • 265
  • More importantly it's evaluated strictly according to [operator precedence](https://en.cppreference.com/w/cpp/language/operator_precedence), so it's not always left to right. `a + b * c` is right (`b * c`) then left (`a + ...`). With a single `&&` it's left to right with short-circuit evaluation. – tadman May 06 '19 at 20:03
  • @tadman: you're confusing evaluation order with precedence... in `a() + b()*c()` the compiler is for example even free to generate code that will call `b()` first, then `a()`, then `c()` and that finally will compute the result. – 6502 May 06 '19 at 20:08
  • A good point, but only because `+` and `*` don't short-circuit. I'm trying to think of an example where `&&` is involved but it's not strictly left-to-right because of precedence issues. – tadman May 06 '19 at 20:09
  • 1
    @tadman: `&&`, `||` and the ternary operator `?:` where in C++ the only ones guaranteeing evaluation order (and only when not overloaded). Later C++17 added a few others, for example `<<` and `>>` because it was very surprising for people that `std::cout << a() << b()` could call `b()` first. – 6502 May 06 '19 at 20:13
  • Great examples! – tadman May 06 '19 at 20:29
  • I believe the comma operator also specifies operand evaluation order. – François Andrieux May 06 '19 at 21:08
  • @FrançoisAndrieux: sorry... you're right, of course the comma operator was also guaranteeing evaluation order. Now (17) there are quite a few more (e.g. `operator[]`) – 6502 May 07 '19 at 06:09