Operators, Expressions, and Evaluation Order
This domain asks you to parse an expression the way the compiler does - precedence, associativity, operand grouping - and then decide whether the result is well-defined, unspecified, or undefined.
Precedence and associativity
- Tightest: postfix
() [] . ->; then unary++ -- + - ! ~ * & sizeof(right-associative); then casts; then* / %; then+ -; then shifts<< >>; then relational< <= > >=; then== !=; then&,^,|; then&&,||; then?:(right-associative); then assignment and compound assignment (right-associative); then the comma operator, which is lowest. - Shifts bind tighter than relationals, so
x << 1 < ymeans(x << 1) < y. - Maximal munch:
x+++ylexes asx++ + y.
Sequencing rules
&&,||,?:, and the comma operator sequence their left operand before their right one.- A full expression (expression statement, initializer,
if/whilecondition, return value) is sequenced before the next statement. - Function-call arguments are unsequenced relative to one another; the left and right operands of
+are unsequenced relative to each other. - Modifying an object twice, or modifying and reading it, without an intervening sequence point is undefined behaviour.
Worked example
int a = 1, b = 2, c = 3;
int x = a < b ? b, c : a;
The middle operand of ?: is parsed as if parenthesized, so this is a < b ? (b, c) : a. The comma expression evaluates b, discards it, and yields c, so x == 3. Compare x = a < b ? b : a, c;, which groups as (x = (a<b ? b : a)), c because assignment binds tighter than the comma - x gets 2 and c is evaluated and discarded.
Traps
if (x = 1)assigns; the missing second=is the classic defect.a & b == 0meansa & (b == 0);!binds tighter than&&.- Shifting by a negative count or by a count at least the type's width is undefined; right-shifting a negative value is implementation-defined.
- A signed left shift that overflows is undefined, as is signed addition overflow.
i = i++ + 1andprintf("%d %d", i++, i++)are undefined, not "left to right" or "right to left".