Is there anything in the C standard (I guess at the moment that's C99 + TC1-3 C11) that guarantees that &
and |
will not be short-circuited?
If I write:
x = y & foo();
...I expect foo
will always get called, but is that really defined? In theory, barring the standard saying otherwise, if y
contained 0
, a runtime optimization could skip the call in the absense of something saying that's not allowed. (And similarly with |
, you could ignore the right-hand operand if the left-hand operand were already all-bits-on. For that matter, even x = y * foo();
could be short-circuited if y
were 0
.)
Not knowing the specification well (and I don't), it's tricky to prove a negative like that. I can contrast the sections on &
(6.5.10 in C99) and &&
(6.5.13 in C99). In the latter, it's perfectly clear:
Unlike the bitwise binary
&
operator, the&&
operator guarantees left-to-right evaluation; there is a sequence point after the evaluation of the first operand. If the first operand compares equal to0
, the second operand is not evaluated.
...but 6.5.10 doesn't specifically state the negative version of that.
It seems reasonable to me to take the fact that 6.5.10 doesn't define a sequence point to mean that foo
will always get called and an implementation that didn't call it would be non-standard. Am I right about that?
Short-Circuit Evaluation: Short-circuiting is a programming concept in which the compiler skips the execution or evaluation of some sub-expressions in a logical expression. The compiler stops evaluating the further sub-expressions as soon as the value of the expression is determined.
Like C, Perl provides the && (logical AND) and || (logical OR) operators. They evaluate from left to right (with && having slightly higher precedence than || ) testing the truth of the statement.
In C++ short-circuiting occurs while evaluating '&&' (AND) and '||'(OR) logical operators. While evaluating '&&' operator if the left-hand side of '&&' gives false, then the expression will always yield false irrespective of the value of the right-hand side of '&&', so checking right-hand side of '&&' makes no sense.
It seems reasonable to me to take the fact that 6.5.10 doesn't define a sequence point to mean that foo will always get called and an implementation that didn't call it would be non-standard. Am I right about that?
Yes and no. Indeed, the implementation that wouldn't call foo would be nonstandard. However, it doesn't have anything to do with sequence points.
The paragraph that would apply here would be 5.1.2.3/3:
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
In this case, sequence points have nothing to do with it. The C standard defines the way the logical machine behaves, and requires that an implementation behave as if it followed that behavior; any optimizations must be transparent with regards to the behavior of the program.
The only operators in the C language which are defined not to evaluate (all of) their operands are ?:
, &&
, ||
, and sizeof
. The only way an implementation could short-circuit |
or &
is if it determined (1) that the value of a single operand is sufficient to know the result, or at least to know if the result is zero or nonzero when the result is only being used as a truth value, and (2) that the other operand has no side effects, and thus the behavior of not evaluating it is the same as if it was evaluated.
With a function call, it's unlikely that the compiler could determine it has no side effects unless it's either static
or flagged with a compiler-specific attribute like gcc's __attribute__((const))
.
Edit: From C99, 5.1.2.3:
In the abstract machine, all expressions are evaluated as specified by the semantics. An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object).
The operators which do not evaluate their operands are then explicitly documented as such.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With