Why this matters
Arithmetic is the first thing anyone expects a computer to do correctly, and in C it mostly does. The exceptions are where bugs hide: division between integers that silently throws away the fraction, a remainder that comes out negative, a calculation grouped differently than you read it, or a division by zero that kills the program. These are among the most common bugs in beginner C code, and the fixes are simple once you know the rules.
The five arithmetic operators
| operator | meaning | example | value |
|---|---|---|---|
+ | addition | 7 + 2 | 9 |
- | subtraction (and unary minus, -x) | 7 - 2 | 5 |
* | multiplication | 7 * 2 | 14 |
/ | division | 7 / 2 | 3 (integers!) |
% | remainder (integers only) | 7 % 2 | 1 |
An expression is anything that produces a value: a literal (7), a variable (x), or operators combining smaller expressions (x * 2 + 1). Expressions appear on the right of =, as function arguments, in conditions, and so on.
Integer division
When both operands of / are integers, the result is an integer: the fractional part is discarded, rounding toward zero.
#include <stdio.h>
int main(void)
{
printf("%d %d %d\n", 17 / 5, 17 % 5, -17 / 5);
printf("%d %d\n", -17 % 5, 17 % -5);
printf("%f %f\n", 17 / 5.0, 17.0 / 5);
return 0;
}
3 2 -3
-2 2
3.400000 3.400000
What happens underneath: the CPU has separate instructions for integer division (idiv on x86-64, which produces a quotient and a remainder together) and for floating-point division (divsd). The compiler picks one based on the types of the operands, not on where the result is going. If either operand is a double, the other is converted to double first and the floating-point instruction is used. So 17 / 5.0 is 3.4, but 17 / 5 is 3 even if you store it in a double.
The remainder operator % gives what is left over: 17 % 5 is 2 because 17 = 3 * 5 + 2. C guarantees the identity
(a / b) * b + a % b == a
and, since division truncates toward zero, the remainder takes the sign of the left operand: -17 % 5 is -2. % works only on integers; for doubles, use fmod from <math.h>.
Integer division and remainder are workhorses. They split quantities into units:
int total_seconds = 3725;
int hours = total_seconds / 3600; /* 1 */
int minutes = total_seconds % 3600 / 60; /* 125 / 60 = 2 */
int seconds = total_seconds % 60; /* 5 */
printf("%d:%02d:%02d\n", hours, minutes, seconds);
1:02:05
They also extract digits (n % 10 is the last decimal digit, n / 10 drops it), test divisibility (n % 2 is 0 for even numbers), and wrap around ((i + 1) % size cycles through 0, 1, ..., size - 1).
Precedence and associativity
How does C read 2 + 3 * 4? Like mathematics: *, / and % have higher precedence than + and -, so it is 2 + (3 * 4) = 14. For operators of the same precedence, associativity decides; the arithmetic operators associate left to right:
10 - 4 - 3 is (10 - 4) - 3 = 3, not 10 - (4 - 3) = 9
100 / 10 / 5 is (100 / 10) / 5 = 2
7 / 2 * 2 is (7 / 2) * 2 = 6 (integer division first!)
The compiler builds a tree from these rules and evaluates from the leaves up:
2 + 3 * 4 (2 + 3) * 4
+ *
/ \ / \
2 * + 4
/ \ / \
3 4 2 3
= 14 = 20
Unary minus binds tighter than any of them: -2 * 3 is (-2) * 3. C has many more precedence levels (about 15); nobody memorizes them all. Use parentheses whenever a reader might hesitate. They do not cost anything at run time.
Division by zero
Integer division by zero is undefined behavior. On x86-64 Linux the CPU raises a fault and the kernel kills the process:
#include <stdio.h>
int main(void)
{
int zero = 0;
printf("before\n");
int x = 10 / zero;
printf("%d\n", x);
return 0;
}
$ ./prog
before
Floating point exception (core dumped)
$ echo $?
136
The message says "floating point" for historical reasons; the signal is SIGFPE, and 136 is 128 + 8, the shell's way of saying "killed by signal 8". The same applies to % by zero, and to INT_MIN / -1, whose result does not fit in an int. If the divisor is a literal 0, gcc warns at compile time (division by zero [-Wdiv-by-zero]); if it comes from a variable or input, only you can check. (Floating-point division by zero is different: it gives inf or nan, as you saw in the previous module.)
Note: when the output goes to a pipe or file instead of a terminal,
beforemay not appear at all: output is held in a buffer and the crash discards it. Buffering gets its own lesson in the input/output module.
Common mistakes
- Integer division when you wanted a fraction: The division happens before the assignment, in
int. Write/ 3.0, or convert one operand (the next lesson shows casts).double avg = (a + b + c) / 3; /* computed in int, then converted */ - Expecting Python's rounding. In C,
-7 / 2is -3 and-7 % 2is -1. In Python they are -4 and 1. Code that checks "is odd" withn % 2 == 1fails for negative odd numbers in C; testn % 2 != 0instead. - Wrong grouping:
total / count * 100for a percentage gives 0 whenever total < count, because the integer division happens first. Writetotal * 100 / count(watch for overflow) or use doubles. - Using
%with doubles: Usefmod.error: invalid operands to binary % (have 'double' and 'int') - Dividing by a value that can be zero. Check first.
Recap
+ - * / %are the arithmetic operators;%is integers only.- Integer
/truncates toward zero;%takes the sign of the left operand, and(a / b) * b + a % b == a. - The operand types, not the destination, decide whether division is integer or floating point.
* / %bind tighter than+ -; equal precedence groups left to right; use parentheses.- Integer division by zero is undefined and normally crashes with SIGFPE.
Going further: write a few expressions mixing / and % with negative numbers and predict each result before running them. Then look up the full C operator precedence table on cppreference.com ("C Operator Precedence") and keep it bookmarked.