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.