Mastering C's expression rules goes beyond memorizing the precedence table. It involves understanding common pitfalls where the rules are non-intuitive and recognizing the critical difference between how an expression is parsed and the order in which its parts are actually executed.
1. The Full Precedence Table (Key Operator Groups)
This is a more comprehensive view of C's operator precedence, from highest to lowest. Operators in the same group have the same precedence.
Precedence |
Category |
Operators |
Associativity |
1 (Highest) |
Postfix |
|
Left-to-Right |
2 |
Unary |
|
Right-to-Left |
3 |
Multiplicative |
|
Left-to-Right |
4 |
Additive |
|
Left-to-Right |
5 |
Bitwise Shift |
|
Left-to-Right |
6 |
Relational |
|
Left-to-Right |
7 |
Equality |
|
Left-to-Right |
8 |
Bitwise AND |
|
Left-to-Right |
9 |
Bitwise XOR |
|
Left-to-Right |
10 |
Bitwise OR |
` |
` |
11 |
Logical AND |
|
Left-to-Right |
12 |
Logical OR |
` |
|
13 |
Conditional (Ternary) |
|
Right-to-Left |
14 |
Assignment |
|
= |
15 (Lowest) |
Comma |
|
Left-to-Right |
聽
2. Common Precedence Pitfalls
Certain operator combinations are notoriously counter-intuitive and a common source of bugs.
Bitwise vs. Equality Operators
A classic error is
to forget that ==
has higher precedence than &
.
路聽聽聽聽聽聽聽聽 Incorrect Code:
C
// Goal: Check if the 'READ_ONLY' bit is set in a 'flags' variable
if (flags & READ_ONLY == READ_ONLY) { ... }
路聽聽聽聽聽聽聽聽
How
it's Parsed: if (flags &
(READ_ONLY == READ_ONLY)) { ... }
which becomes if
(flags & 1) { ... }
. This is a
completely different logical check.
路聽聽聽聽聽聽聽聽 Correct Code:
C
if ((flags & READ_ONLY) == READ_ONLY) { ... }
Assignment vs. Relational Operators
The assignment
operator (=
) has very low precedence.
路聽聽聽聽聽聽聽聽 Incorrect Code:
C
// Goal: Read characters until End-Of-File
while (c = getchar() != EOF) { ... }
路聽聽聽聽聽聽聽聽
How
it's Parsed: while (c =
(getchar() != EOF)) { ... }
. The
comparison getchar()
!= EOF
results in either 1
or 0
. This 1
or 0
is then assigned to c
. The loop will run correctly but the character value is lost.
路聽聽聽聽聽聽聽聽 Correct Code:
C
while ((c = getchar()) != EOF) { ... }
size=2 width="100%" align=center>
3. Precedence vs. Order of Evaluation (A Critical Distinction)
This is the most important advanced concept.
路聽聽聽聽聽聽聽聽 Precedence and associativity determine how expressions are grouped. They dictate which operands belong to which operators.
路聽聽聽聽聽聽聽聽 Order of evaluation determines which sub-expression's value is computed first.
With the exception
of &&
, ||
, ?:
, and the comma operator, the C standard does not
specify the order of evaluation for sub-expressions. Relying on a
left-to-right evaluation can lead to undefined behavior.
Example: Unspecified Order of Evaluation
C
int i =
5;
// Which 'i' is accessed first? The one for the index, or the one for the increment?
// The C standard does not say. This is UNDEFINED BEHAVIOR.
// One compiler might do the increment first, another might do the assignment first.
array[i] = i++;
This
code is syntactically valid but logically broken. Precedence rules parse it as (array[i]) = (i++)
, but they do not guarantee when the side effect of i++
occurs relative to the use of i
as an array index. Never modify a variable that is
used elsewhere in the same expression without an intervening sequence point.
4. The Comma Operator
The comma operator has the lowest precedence of all. It is a sequence point.
路聽聽聽聽聽聽聽聽 As an Operator: It evaluates its left operand, discards the result, then evaluates its right operand and takes that value as the result of the expression.
路聽聽聽聽聽聽聽聽
As a
Separator: In function calls (f(a, b)
) and declarations (int a, b;
), the comma is a separator, not an operator.
Its primary use is to fit multiple sequential operations into a place where C syntax expects a single expression.
Example: Multiple
initializations/increments in a for
loop
C
#include <stdio.h>
聽
int main() {
聽聽聽
int i, j;
聽聽聽
// The comma operator allows multiple expressions in the init and increment parts
聽聽聽
for (i =
0, j =
10; i < j; i++, j--) {
聽聽聽聽聽聽聽
printf(
"i=%d, j=%d\n", i, j);
聽聽聽 }
聽聽聽
return
0;
}
聽
聽
An advanced understanding of C arithmetic expressions involves the language's strict rules for type promotion, the defined and undefined behaviors of overflow, and the sequencing of side effects.