Short-Circuit Evaluation in C: Logical AND and OR

Short-circuit evaluation is a control-flow feature in the C programming language where the second operand of a logical operation is executed only when the first operand is insufficient to determine the overall result. This article explains the exact mechanisms behind short-circuiting with the logical AND (&&) and logical OR (||) operators, the guarantee of sequence points, practical coding patterns like safe pointer dereferencing, and the risks associated with expressions containing side effects.

How Logical AND (&&) Short-Circuits

In C, the logical AND operator (&&) yields a result of 1 (true) only if both operands evaluate to a non-zero value. If the left-hand operand evaluates to 0 (false), the entire expression is guaranteed to be false regardless of the right-hand operand's value.

Consequently, the compiler skips the evaluation of the right-hand operand entirely when the left-hand side is 0.

if (ptr != NULL && *ptr == 10) {
    // Executes safely
}

In the example above, if ptr is NULL, the left operand ptr != NULL evaluates to 0. The right operand *ptr == 10 is never evaluated, successfully avoiding a null-pointer dereference and a program crash.

How Logical OR (||) Short-Circuits

The logical OR operator (||) yields a result of 1 (true) if at least one operand evaluates to a non-zero value. If the left-hand operand evaluates to any non-zero value (true), the entire expression is guaranteed to be true.

Under this condition, the compiler does not evaluate the right-hand operand.

if (is_cached || fetch_from_database()) {
    // Executes if cached or successfully fetched
}

Here, if is_cached is non-zero, the expression evaluates to true immediately, and the expensive fetch_from_database() function call is bypassed entirely.

Evaluation Order and Sequence Points

The C standard explicitly defines that logical AND and logical OR operators enforce a left-to-right evaluation order. Furthermore, there is a sequence point introduced immediately after the evaluation of the first operand.

This sequence point guarantees that all side effects of the left-hand operand—such as variable assignments or increments—are fully committed before the right-hand operand is evaluated (if it is evaluated at all).

The Danger of Side Effects

While short-circuiting provides safety and performance optimizations, it can introduce logic bugs when expressions rely on side effects, such as the increment (++) and decrement (--) operators or state-modifying functions.

int a = 1;
int b = 0;

if (a || b++) {
    // b++ is never executed because 'a' is non-zero
}

In this scenario, b remains 0 because the truth of a terminates evaluation before b++ is reached. To write robust C code, avoid placing operations with side effects on the right-hand side of && and || operators unless the conditional execution of that side effect is explicitly intended.