Module 4 · Operators and Type Conversion
Precedence and Associativity: When to Reach for Brackets
In this lesson
- Read the precedence table as a reference you look things up in, not a list you memorise.
- State the five rules that settle the order in almost every line you will write.
- Add brackets wherever a reader would have to stop and think.
Zara is checking whether a flag byte has its lowest bit clear. She writes x & 1 == 0, which reads exactly like the sentence.
The Playground compiles it without a word. For x of 4, where the answer should be 1, it gives 0. For every other value it also gives 0.
The operators are right and the order is wrong. == grabs the operands before & gets near them.
Precedence decides who gets the operands
In 2 + 3 * 4 the number 3 sits between two operators. Only one of them can have it.
Precedence is the ranking that settles the argument. * outranks +, so 3 goes to the multiplication.
#include <stdio.h>
int main(void)
{
printf("2 + 3 * 4 = %d\n", 2 + 3 * 4);
printf("(2 + 3) * 4 = %d\n", (2 + 3) * 4);
return 0;
}
2 + 3 * 4 = 14
(2 + 3) * 4 = 20
Brackets outrank everything. They are not a hint to the compiler; they change which operator gets the operand.
So precedence is not about what runs first in time. It is about how the line is grouped before anything runs at all.
The table, fifteen rows, highest first
Here is the part of C's table that this track has taught or will teach. Look things up in it; do not learn it.
| Row | Operators | Groups | First met in |
|---|---|---|---|
| 1 | () [] . -> postfix ++ -- | left to right | M1, M9, M12 |
| 2 | unary ! ~ + - prefix ++ -- (type) sizeof | right to left | M2, lesson 3 |
| 3 | * / % | left to right | lesson 1 |
| 4 | + - | left to right | lesson 1 |
| 5 | << >> | left to right | lesson 4 |
| 6 | < <= > >= | left to right | lesson 2 |
| 7 | == != | left to right | lesson 2 |
| 8 | & | left to right | lesson 4 |
| 9 | ^ | left to right | lesson 4 |
| 10 | | | left to right | lesson 4 |
| 11 | && | left to right | lesson 2 |
| 12 | || | left to right | lesson 2 |
| 13 | ? : | right to left | lesson 2 |
| 14 | = += -= *= /= %= | right to left | M2, lesson 3 |
| 15 | , | left to right | this lesson's teaser |
Two things in that table are worth a second look, and both are the traps of this lesson.
Rows 6 and 7 sit above rows 8 to 10. Every comparison binds tighter than every bitwise operator.
Row 5 sits below rows 3 and 4. Every shift binds looser than every ordinary sum.
Associativity: which way a row leans
Precedence settles arguments between different rows. Associativity settles them inside one row.
Rows 3 to 12 group left to right. 20 - 8 - 3 is (20 - 8) - 3, which is 9, and not 15.
Rows 2, 13 and 14 group right to left. That is why p = q = r = 7 works: the rightmost assignment happens first and its value flows left.
#include <stdio.h>
int main(void)
{
int a = 20;
int b = 8;
int c = 3;
printf("a - b - c = %d\n", a - b - c);
printf("a - (b - c) = %d\n", a - (b - c));
int p = 0;
int q = 0;
int r = 0;
p = q = r = 7;
printf("p %d q %d r %d\n", p, q, r);
return 0;
}
a - b - c = 9
a - (b - c) = 15
p 7 q 7 r 7
Subtraction leaning the other way would break school arithmetic, so left to right is the one you already expect.
So associativity only matters when you have written two operators of the same rank in a row, which is most lines of arithmetic.
The two traps that really bite
Almost every precedence bug in real C is one of two shapes, and the table already told you both.
Trap one: a comparison beside a bitwise operator. Rows 6 and 7 are above row 8, so the comparison wins.
#include <stdio.h>
int main(void)
{
unsigned int x = 4u;
printf("x & 1u == 0u %u\n", x & 1u == 0u);
printf("(x & 1u) == 0u %u\n", (x & 1u) == 0u);
return 0;
}
x & 1u == 0u 0
(x & 1u) == 0u 1
The first line is x & (1u == 0u), which is x & 0, which is 0 for every x there has ever been.
The Playground says nothing. A local gcc -Wall reports warning: suggest parentheses around comparison in operand of '&', which is the compiler telling you the table's answer.
Trap two: a shift beside a sum. Row 5 is below row 4, so the addition happens first.
#include <stdio.h>
int main(void)
{
printf("1u << 2 + 1 = %u\n", 1u << 2 + 1);
printf("(1u << 2) + 1 = %u\n", (1u << 2) + 1);
return 0;
}
1u << 2 + 1 = 8
(1u << 2) + 1 = 5
The first is 1u << 3. The two answers are 8 and 5, and only one of them is a power of two, which is how this one is usually noticed.
So the two traps share a cause: C ranked its operators in an order that does not match how the sentences are read aloud.
Five rules, and one bracket rule
You do not need the table for everyday code. Five statements cover almost all of it.
The five rules
1 unary before binary -a * b is (-a) * b
2 * / % before + - a + b * c is a + (b * c)
3 comparison, then &&, then || a < b && c < d needs no brackets
4 assignment is last, and leans right p = q = 7
5 brackets win, always
- The five say nothing about the bitwise operators or the shifts. That is deliberate: those are the traps.
- Rule 3 is why a range check reads cleanly without a single bracket.
- Rule 5 is the one you use when the other four leave you unsure.
The track's style rule follows from that gap: bracket every bitwise operator and every shift when anything else shares the line.
Brackets cost nothing at run time. The compiler produces identical machine code, so the only reader they serve is human.
One line, two readings, both printed.
#include <stdio.h>
int main(void)
{
int n = 0;
scanf("%d", &n);
printf("%d\n", 10 - 4 > n);
printf("%d\n", 10 - (4 > n));
return 0;
}
1
9
That output is for the input 3. The first is a yes or no; the second is a number near ten. Arithmetic outranks comparison, so the first line is what C meant.
The bug and the fix side by side, on whatever value you feed it.
#include <stdio.h>
int main(void)
{
unsigned int x = 0;
scanf("%u", &x);
printf("no brackets %u\n", x & 1u == 0u);
printf("bracketed %u\n", (x & 1u) == 0u);
return 0;
}
no brackets 0
bracketed 1
That output is for the input 4. Feed it 5 and both lines print 0, which is exactly why the bug survives testing.
Distance under constant acceleration. The formula is written with no brackets beyond the ones the algebra needs.
#include <stdio.h>
int main(void)
{
double u = 0.0;
double t = 0.0;
double a = 0.0;
scanf("%lf %lf %lf", &u, &t, &a);
double distance = u * t + 0.5 * a * t * t;
printf("%.2f\n", distance);
return 0;
}
80.10
That output is for the input 12 3 9.8. Rule 2 does all the work: every multiplication binds before the one addition, which is exactly what the algebra says.
Where this is used
- Every kernel flag test. Linux is full of lines like
(flags & O_APPEND) != 0. The brackets are there because of exactly the trap in this lesson. - Coding standards. MISRA C, the standard used in cars and medical devices, has a rule requiring brackets around mixed operators, for the same reason this track does.
- Compiler warnings. GCC's
-Wparenthesesexists solely for these two shapes, and it is switched on by-Wallin almost every real project. - Reading someone else's code. The table is how you answer "what does this line actually do" without running it, which is most of code review.
Common mistakes
1. Comparing inside a mask test.
unsigned int x = 4u;
printf("%u\n", x & 1u == 0u);
Silent on the Playground; a local gcc -Wall says warning: suggest parentheses around comparison in operand of '&'. It prints 0 for every value. Bracket the mask: (x & 1u) == 0u.
2. Adding to a shift count by accident.
printf("%u\n", 1u << 2 + 1);
Silent on the Playground; a local gcc -Wall says warning: suggest parentheses around '+' inside '<<'. It prints 8, not 5. Bracket the shift.
3. Mixing the three bitwise operators without brackets.
printf("%d\n", 1 | 2 ^ 3 & 4);
Silent on the Playground; gcc -Wall gives two suggest parentheses warnings. It prints 3, because & binds tighter than ^, which binds tighter than |. Nobody reads that correctly at speed, so bracket it.
4. Assuming precedence decides what runs first in time.
int r = f() + g() * h();
No message at either command line. Precedence says the product of g() and h() joins the sum, and nothing at all about which function is called first. Lesson 3's rule applies: do not depend on that order.
Kenji wants to check that he can read a line the way C reads it.
Given three integers a, b and c, print the values of these five expressions, in this order, one per line: a + b * c, a - b - c, a * b % c, a < b && b < c, a + b > c.
Input. One line with three integers.
Output. Five lines, the five values in the order above.
Constraints. -1000 <= a, b <= 1000, and 1 <= c <= 1000.
Sample. Input 2 3 4 gives the five lines 14, -5, 2, 1, 1.
#include <stdio.h>
int main(void)
{
int a = 0;
int b = 0;
int c = 0;
scanf("%d %d %d", &a, &b, &c);
/* Type the five expressions exactly as written. Add no brackets. */
return 0;
}
Graded as five-values. Work out all five on paper first; the hidden tests include negative values, where the third one is worth checking.
Amara inherited three broken lines and has to make each one mean what its comment says.
The three lines are written x & 1 == 0, 1 << 2 + n and x ^ 1 != 0. They are meant to answer: is bit 0 of x clear; what is four plus n; is x with its lowest bit flipped still nonzero.
Add brackets, and change nothing else, so each line means what it was meant to.
Input. One line with two integers x and n.
Output. Three lines, the corrected value of each expression in the order given.
Constraints. 0 <= x <= 1000, and 0 <= n <= 20.
Sample. Input 4 3 gives the three lines 1, 7, 1.
#include <stdio.h>
int main(void)
{
unsigned int x = 0;
unsigned int n = 0;
scanf("%u %u", &x, &n);
/* Each line needs exactly one pair of brackets, around the bitwise part. */
return 0;
}
Not graded in this module. Run each line with and without its brackets and keep both answers; that pair is the lesson.
Run in CompilerZara is typing a formula from a physics book and wants it right the first time.
The kinetic energy of a body is half its mass times the square of its speed. She also wants that value divided by the time given.
Input. One line with three decimal values: mass, speed and time.
Output. Two lines: the kinetic energy, then the energy divided by the time, each to exactly two decimal places.
Constraints. 0 < mass <= 1000, 0 <= speed <= 1000, 0 < time <= 1000.
Sample. Input 2 10 4 gives 100.00, then 25.00.
#include <stdio.h>
int main(void)
{
double mass = 0.0;
double speed = 0.0;
double time = 0.0;
scanf("%lf %lf %lf", &mass, &speed, &time);
/* Rule 2 does the first line. The second needs one pair of brackets. */
return 0;
}
Not graded in this module. Writing the second line without brackets divides only the last factor, and the answer is wrong by a factor you should be able to name.
Run in CompilerCommon doubts
Do extra brackets slow the program down?
No. They exist only in the text; the compiler produces the same instructions either way. They cost typing and nothing else.
Should I memorise all fifteen rows?
No. Learn the five rules and the two traps, and look the rest up. Professional C programmers reach for the table too.
Why did C rank the bitwise operators below the comparisons?
A historical accident. The operators existed before
&&and||did, and changing the ranking later would have broken existing programs.Is
a < b < ca precedence problem?No, it is an associativity one. Both
<signs are row 6 and group left to right, which is lesson 2's chained comparison.Does precedence tell me which function is called first?
No. Grouping and order of evaluation are different questions, and C leaves the second one open. Lesson 3's rule covers it.
Key takeaways
- Precedence decides grouping, not the order things happen in time.
- Comparisons bind tighter than
&,^and|, which is trap one. - Shifts bind looser than
+and-, which is trap two. - Most rows group left to right; unary, the ternary and assignment lean right.
- Five rules cover everyday arithmetic and logic without the table.
- This track brackets every shift and every bitwise operator that shares a line.
Next you find out what happens when the two operands of an operator are not the same type at all.
End of lesson 5
Mark it done, and your progress moves with you.
Next: Type Conversion: Promotion, Casting and Silent Loss