C · Beginner
Operators in C
In short: C operators build expressions from values: arithmetic (+ - * / %), comparison (== != < > <= >=), logical (&& || !), assignment (= += -= *= /=) and increment (++ --). Two rules cause most surprises: dividing two integers drops the fraction, and a comparison produces the plain integers 1 and 0, so any non-zero value counts as true.
Expressions and what they produce
An expression is anything that produces a value: a literal such as 12, a variable, or an operator applied to smaller expressions.
The arithmetic operators are +, -, *, / and %. When both operands of / are integers, the result is an integer and the fraction is discarded: 7 / 2 is 3, and since C99 the truncation is always toward zero, so -7 / 2 is -3. % gives the remainder of that division and takes the sign of the left operand: 7 % 3 is 1 and -7 % 3 is -1. It only works on integers. If either operand is a double the whole operation is done in floating point, so 7 / 2.0 is 3.5. To get a fraction from two integer variables, convert one first with a cast: (double) total / count. The cast binds tighter than /, so it converts total alone, before the division.
Comparison operators, ==, !=, <, >, <= and >=, produce an int that is 1 when the comparison holds and 0 when it does not. if and loops simply test whether an expression is non-zero. The logical operators combine those results: a && b is 1 only if both are non-zero, a || b is 1 if either is, and !a turns 0 into 1 and anything else into 0. && and || evaluate the left side first and skip the right side when it cannot change the answer. This short-circuit rule makes count > 0 && total / count > 10 safe: the division never runs when count is zero.
Assignment = stores the right value in the left variable and, being an expression, also produces that value. Compound forms shorten the common update: total += x is total = total + x, and -=, *=, /= and %= work the same way. x++ and ++x both add 1 to x; the difference is what the expression yields. x++ yields the old value and then increments, ++x increments first and yields the new value. On a line of its own either form does the same job.
The conditional operator condition ? a : b picks one of two values and is handy inside a printf argument. sizeof is an operator too, not a function. The bitwise operators &, |, ^, ~, << and >> act on individual bits and are easy to confuse with && and ||.
Precedence decides which operator applies first when there are no parentheses: unary operators and casts bind tightest, then * / %, then + -, then comparisons, then &&, then ||, then ?:, and assignment last. a + b * c multiplies first. When a line mixes && with ||, or comparisons with arithmetic, add parentheses even where they are not strictly needed.
Syntax
a + b a - b a * b a / b a % b /* arithmetic; int / int truncates */
a == b a != b a < b a > b a <= b a >= b /* comparison: yields 1 or 0 */
a && b a || b !a /* logical; && and || short-circuit */
x = v x += v x -= v x *= v x /= v x %= v /* assignment */
x++ ++x x-- --x /* increment and decrement */
cond ? a : b /* conditional: picks a or b */
(double) a / b /* cast one operand before dividing */% and the bitwise operators need integer operands. / on two ints gives an int; on anything involving a double it gives a double.
Precedence from highest to lowest
| Level | Operators | Note |
|---|---|---|
| 1 | () grouping, [], function call | always applied first |
| 2 | !, unary -, ++, --, (type), sizeof | apply to the single operand on their right |
| 3 | * / % | left to right |
| 4 | + - | left to right |
| 5 | < <= > >= | produce 1 or 0 |
| 6 | == != | lower than <, so a == b < c means a == (b < c) |
| 7 | && | binds tighter than || |
| 8 | || | the loosest logical operator |
| 9 | ?: | groups right to left |
| 10 | = += -= *= /= %= | right to left; lowest of all |
Integer division and remainder
A departure time stored as minutes after midnight is split into hours and minutes, then an occupancy percentage is computed both ways.
#include <stdio.h>
int main(void)
{
int departure = 517; /* minutes after midnight */
int hours = departure / 60;
int minutes = departure % 60;
printf("Departure: %02d:%02d\n", hours, minutes);
int seats = 45, passengers = 32;
printf("Occupancy (int): %d\n", passengers * 100 / seats);
printf("Occupancy (double): %.1f%%\n", (double) passengers * 100 / seats);
printf("-7 / 2 = %d, -7 %% 2 = %d\n", -7 / 2, -7 % 2);
return 0;
}
Output
Departure: 08:37 Occupancy (int): 71 Occupancy (double): 71.1% -7 / 2 = -3, -7 % 2 = -1
517 / 60 is 8 because the fraction is dropped, and 517 % 60 is the 37 minutes left over; %02d pads each number to two digits with a leading zero. passengers * 100 / seats is 3200 / 45, which is 71 in integer arithmetic; casting passengers to double first makes the whole calculation floating point, giving 71.1. The last line shows the sign rules: division truncates toward zero and the remainder keeps the sign of the left operand. %% writes a literal percent sign.
Comparisons produce 1 and 0; && short-circuits
Two greenhouse readings are turned into flags, combined, and then used to show that the right side of && is skipped when the left is 0.
#include <stdio.h>
int main(void)
{
int temp = 24, humidity = 70;
int warm = temp > 20;
int dry = humidity < 50;
printf("warm = %d, dry = %d\n", warm, dry);
printf("warm && dry = %d\n", warm && dry);
printf("warm || dry = %d\n", warm || dry);
printf("!dry = %d\n", !dry);
int count = 0;
int result = dry && (count = 5);
printf("result = %d, count = %d\n", result, count);
return 0;
}
Output
warm = 1, dry = 0 warm && dry = 0 warm || dry = 1 !dry = 1 result = 0, count = 0
temp > 20 is stored straight into an int: it is simply the value 1. The logical operators then combine those ints, and !dry flips 0 to 1. In the last statement dry is 0, so && already knows the answer and never evaluates (count = 5); count stays 0. The same rule lets a test guard an expression that must not run, such as a division by a count that might be zero.
Compound assignment and increment
A stock count is updated with the compound operators, then the two increment forms are compared.
#include <stdio.h>
int main(void)
{
int loaves = 10;
loaves += 5;
printf("%d\n", loaves);
loaves *= 2;
printf("%d\n", loaves);
loaves -= 4;
loaves /= 2;
printf("%d\n", loaves);
int a = loaves++; /* a gets 13, then loaves becomes 14 */
int b = ++loaves; /* loaves becomes 15, then b gets 15 */
printf("a = %d, b = %d, loaves = %d\n", a, b, loaves);
int price = loaves > 12 ? 3 : 4;
printf("price = %d\n", price);
return 0;
}
Output
15 30 13 a = 13, b = 15, loaves = 15 price = 3
The compound operators take loaves from 10 to 15, then 30, then 26 and finally 13. loaves++ hands back the old value 13 to a and only then makes loaves 14; ++loaves increments to 15 first and gives b that new value. The conditional operator chooses 3 because loaves > 12 is true. The order of yield and update is the whole difference between the two increment forms.
Common mistakes
Using = where == was meant
Why it goes wrong:
if (level = 0)assigns 0 to level and tests the result, 0, so the block never runs; with a non-zero value it would always run. gcc -Wall warns:suggest parentheses around assignment used as truth value.Fix: Compare with
==. Some programmers write the constant first,0 == level, so a missing=becomes a compile error.C · fixif (level == 0) { printf("empty\n"); }Expecting a fraction from integer division
Why it goes wrong:
double average = total / count;divides as integers first and only then converts the result to double: 7 / 2 stored in a double is 3.0, not 3.5.Fix: Cast one operand before dividing.
C · fixdouble average = (double) total / count;Changing a variable twice in one expression
Why it goes wrong:
i = i++;anda[i] = i++;modifyiand read it again in the same expression with no defined order; that is undefined behaviour and different compilers give different results.Fix: Put each update in its own statement: use
i++on a line by itself, ori += 1.Chaining comparisons
Why it goes wrong:
if (0 <= x <= 9)compiles but means(0 <= x) <= 9: the first comparison gives 0 or 1, both of which are at most 9, so the test is always true.Fix: Join two comparisons with
&&.C · fixif (0 <= x && x <= 9) { printf("single digit\n"); }
Money, cycles and rounding
Working in whole pence rather than pounds in a double avoids rounding errors: a price of 3.20 is the int 320, a 12.5 % discount is price * 875 / 1000, and / 100 and % 100 split the result back into pounds and pence for printing. The remainder operator is also the tool for anything cyclic: day % 7 turns a day count into a weekday, minute % 60 wraps minutes, and i % 2 == 0 tests for even numbers. To round an integer division up instead of down, add the divisor minus one before dividing: (items + per_box - 1) / per_box is the number of boxes needed.
int pence = 320;
int discounted = pence * 875 / 1000; /* 12.5 % off, stays an int: 280 */
printf("%d.%02d\n", discounted / 100, discounted % 100);
int boxes = (items + per_box - 1) / per_box; /* round up */Key points
/on two integers discards the fraction; cast one operand to double to keep it.%is the integer remainder and takes the sign of the left operand.- Comparisons and logical operators produce the ints 1 and 0; any non-zero value is true.
&&and||stop evaluating as soon as the answer is known.x++yields the old value,++xthe new one; alone on a line they are the same.=assigns and==compares; never swap them in a condition.- When in doubt about precedence, add parentheses.
Try it yourself
Read a number of seconds and print it as minutes and seconds in the form 12 min 5 s, using / and %. The input is 725.
#include <stdio.h>
int main(void)
{
int seconds;
if (scanf("%d", &seconds) != 1) {
return 1;
}
/* compute minutes and remaining seconds, then print them */
return 0;
}
72512 min 5 s#include <stdio.h>
int main(void)
{
int seconds;
if (scanf("%d", &seconds) != 1) {
return 1;
}
int minutes = seconds / 60;
int rest = seconds % 60;
printf("%d min %d s\n", minutes, rest);
return 0;
}
Practise this
Exercises for this lesson are in the C practice set.
Related lessons
- Variables and Types in CDeclaring C variables and choosing types: int, long, double, char and bool, sizeof, why uninitialised variables hold garbage, and printf and scanf specifiers.9 min
- Conditions in C: if, else and switchHow C chooses between branches with if, else if, else and switch: what counts as true, combining tests with && and ||, and why each switch case ends with break.9 min
- Loops in C: for, while and do-whileThe three C loops and when each fits: how a for loop's three parts run, reading input until it ends with while and scanf, and using break and continue safely.9 min
Frequently asked questions
Why is 7 / 2 equal to 3 in C?
Because both operands are integers, and C's integer division produces an integer by discarding the fractional part. The language does not switch to floating point on its own; it does so only when at least one operand already is floating point. Write 7 / 2.0, or cast a variable with (double), to get 3.5.
What is the difference between ++i and i++?
Both add 1 to i. ++i (prefix) does the increment and then yields the new value; i++ (postfix) yields the value i had before and increments afterwards. int a = i++; stores the old value in a, while int a = ++i; stores the new one. As a statement on its own, such as the step of a for loop, there is no difference.
Does C have true and false?
Since C99, <stdbool.h> defines the type bool and the constants true and false, and C23 makes them keywords. Underneath, comparisons and logical operators still produce int values 1 and 0, and every test in if, while and for simply asks whether a value is non-zero. So if (count) is legal and means if (count != 0).
How this page was checked. Every program on it was run with GCC 13 (C11) at build time by the publishing checks, and the output shown is what it printed. Running C inside the browser is not available yet, so the Run button is absent rather than pretending; copy the code and run it with GCC 13 (C11) locally.