Previous | Table of Contents | Next |
Weve mentioned at several points that Cs operators have the usual precedence and associativity relations defining how they bind to their operandsfor example, that the expression a + b * c is parsed by the compiler as if it had been written as a + (b * c). This table summarizes the precedence (highest to lowest) and grouping of all of Cs operators (including those having to do with pointers and structures that are formally introduced in sections 3.6 and 3.7).
Class | Operators | Associativity |
---|---|---|
primary | array [] . -> function-call () | left-to-right |
postfix unary | ++ -- | left-to-right |
prefix unary | ++ -- & * + - ~ ! sizeof cast | right-to-left |
multiplicative | * / % | left-to-right |
additive | + - | left-to-right |
shift | << >> | left-to-right |
relational | < > <= >= | left-to-right |
equality | == != | left-to-right |
bitwise AND | & | left-to-right |
exclusive OR | ^ | left-to-right |
bitwise OR | | | left-to-right |
logical AND | && | left-to-right |
logical OR | || | left-to-right |
conditional | ?: | right-to-left |
assignment | = *= /= %= += -= <<= >>= &= ^= |= | right-to-left |
comma | , | left-to-right |
Although it can be convenient to say things like due to precedence, multiplication takes place before addition, it is important to realize that not all aspects of evaluation order are predetermined in C. Precedence and associativity may determine the order of evaluation of operators, but when the operands of a given operator are themselves nontrivial subexpressions, there is nothing in general to tell us the order in which those subexpressions themselves will be evaluated. In the expression
a * b + c * d
the addition happens last, but we do not know the relative order of the two multiplications. In the expression
a[i++] + b[j++] * c[k++]
the addition happens after the multiplication, but the three increments could occur in any order, either before or after the other operations. (If the compiler chose to perform the increments first, it would save the old values of i, j, and k somewhere and use them as the subscripts.) In the expression
f() + g() * h()
the three functions could be called in any order.
The fact that evaluation order is underdetermined means that certain expressions must be avoided. One example is
a[i] = i++
which may look reasonable until we ask whether the i in a[i] is the old or the incremented value. The question is unanswerable: The C Standard says that it is undefined to modify a variable or location, and try to use its value at another point, unless it is definitively known which of these actions will occur first. A similar prohibition applies to modifying the same object twice within an expression, as in i++ * i++.
Use and modification of the same variable is, of course, permitted when the old value is being used to compute the new; otherwise, familiar and reasonable idioms such as i = i + 1 would be illegal. There is also a special exception for the &&, ||, ?:, and comma operators; these four each imply a sequence point after evaluation of their first operand, meaning that the first operand is guaranteed to be completely evaluated before any part of the second is evaluated (if the second operandor, in the case of the ?: operator, the third operandis evaluated at all).
Except when using one of those operators, therefore, it is best not to try to predict the exact order of evaluation within an expression. In particular, it is not possible in the general case to determine the exact sequence of one expressions side effects.
It is tempting to argue that these undefined expressions must be evaluated in some order by any given compiler, and to perform experiments to determine what that order might be. It is perilous, however, to make use of any information so gained in an actual program because there is obviously no guarantee that different compilers (or even the same compiler, under different circumstances) will produce the same results.
A good rule of thumb to follow is that, as mentioned, and with the exception of the four operators that do provide specific guarantees, the only constraints on the actual order of evaluation within an expression are those required in order for the final, topmost value of the expression to be correct. Subexpressions within the expression may in general be evaluated in any order, and any side effects may occur in any order. When, for whatever reason, more control over expression evaluation is needed, the safest approach is often to use separate statements because statements do (by definition) provide absolute control over their order of execution. For example, rather than writing a[i] = i++, write either
a[i] = i - 1; i++;
or
a[i] = i; i++;
or
a[i] = i + 1; i++;
depending on whether a[i] should receive the previous, current, or next value of i.
Previous | Table of Contents | Next |