Reputation: 162164
A recurring pattern in a piece of inherited code is that a chain of functions is called and the chain aborted, as soon as one of the functions returns a certain value. This value shall then be used for following computation. For the sake of demonstration the breaking value is 0
. Consider the following program:
#include <stdio.h>
static int n;
static int foo(int x)
{
fprintf(stderr, "%d(%d) ", x, n++);
return x ? 0 : n;
}
static void with_if(void)
{
int rc;
n = 0;
do {
if( (rc = foo(1)) ) break;
if( (rc = foo(2)) ) break;
if( (rc = foo(3)) ) break;
if( (rc = foo(4)) ) break;
if( (rc = foo(0)) ) break;
if( (rc = foo(5)) ) break;
} while(0);
fprintf(stderr, ">>%d<<\n", rc);
}
void with_short_circuit(void)
{
int rc;
n = 0;
(rc = foo(1))
|| (rc = foo(2))
|| (rc = foo(3))
|| (rc = foo(4))
|| (rc = foo(0))
|| (rc = foo(5));
fprintf(stderr, ">>%d<<\n", rc);
}
int main(int argc, char *argv[])
{
with_if();
with_short_circuit();
return 0;
}
Note that the short circuited variant is not only more concise, it's (IMHO) also far easier to read and reason about, since you don't have to push all the other surrounding statements into your mental stack, when reading that code. So I by large prefer the short circuited variant.
As far as GCC-4.9.3 and Clang-3.6.2 are concerned with_if
and with_short_circuit
are identical (they produce the very same assembly output).
What I'm worried about is, that the outcome of the boolean operator chain is ignored (if compiled with -Wall
GCC emits a warning about it, Clang remains silent though) and that this might be seen as a chance for optimization. Of course calling foo causes a side effect, hence in my understanding of the C language standard is should be safe to use boolean short circuit for control flow like this. Bit I'm not quite sure about it.
Upvotes: 3
Views: 1021
Reputation: 150
I ran a test, the result speaks for itself (figure below).
I, also, expected the compiler to honor function calls with side effects.
Then, consulting the document below, section 5.14 (link to pdf), we find: "Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false." Of course, the OR operator is implemented likewise (applying DeMorgan's law).
Working Draft, Standard for Programming Language C++, N4296
Upvotes: 0
Reputation: 12047
This is called a "discarded-value expression", and it has to be evaluated:
N4296, 5§11:
In some contexts, an expression only appears for its side effects. Such an expression is called a discarded-value expression. The expression is evaluated and its value is discarded.
Upvotes: 5
Reputation: 583
The GCC in fact does make a hard guarantee about the behaviour of any code that produces a side effect. You are quite safe, so long as you are not trying to create a delay or measure execution speed.
However, your code in the while(0) would actually be considered bad design by most. Code that performs that way is usually written like:
if( (rc = foo(1)) ) ;
else if( (rc = foo(2)) ) ;
else if( (rc = foo(3)) ) ;
else if( (rc = foo(4)) ) ;
else if( (rc = foo(0)) ) ;
else if( (rc = foo(5)) ) ;
In addition, it is not usually considered appropriate to put an = inside a conditional expression, if for no other reason than the similarity between == and =.
Upvotes: 0
Reputation: 726509
C/C++ are not allowed to optimize out function calls like this, so you are completely safe. The standard guarantees both the short-circuiting and the order of evaluation of your with_short_circuit
expression, so any standard-compliant compiler will produce the code with the right behavior.
My guess as to why the compiler issues the warning is that a stand-alone expression statement without obvious side effects is usually a mistake. In your case, however, side effects are very obvious. Since you know for sure what you are doing, you can silence this specific warning with a diagnostic pragma.
Upvotes: 3