Operators, expressions and input · about 45 min

Arithmetic operators and integer division

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

operatormeaningexamplevalue
+addition7 + 29
-subtraction (and unary minus, -x)7 - 25
*multiplication7 * 214
/division7 / 23 (integers!)
%remainder (integers only)7 % 21

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, before may 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

Recap

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.

Exercises

Exercise 1 · quiz

Quotient and remainder

What does this print?

printf("%d %d %d %d\n", 17 / 5, 17 % 5, -17 / 5, -17 % 5);
Answer
  • 3 2 -3 -2

Integer division discards the fractional part, rounding toward zero: 17 / 5 is 3 and -17 / 5 is -3 (not -4). The remainder is defined so that (a / b) * b + a % b == a always holds, which makes -17 % 5 equal to -2. So in C the sign of % follows the left operand. (Python rounds down and gives -4 and 3 instead; beware when switching languages.)

Exercise 2 · quiz

Precedence and associativity

What is the value of 20 - 8 / 2 * 3 + 1?

Answer
  • 9

  • and / bind tighter than + and -, and operators of equal precedence group left to right.

So 8 / 2 * 3 is (8 / 2) * 3 = 12, then 20 - 12 + 1 is (20 - 12) + 1 = 9. When in doubt, add parentheses: they cost nothing and make intent explicit.

Exercise 3 · write code

Fix the average

The starter should print the average of three test scores, 7, 8 and 8, but prints 7.00. Fix the calculation (not the printf format) so it prints exactly:

average: 7.67

Keep the scores in int variables.

File: main.c

Starting point

#include <stdio.h>

int main(void)
{
    int a = 7;
    int b = 8;
    int c = 8;
    double avg = (a + b + c) / 3;
    printf("average: %.2f\n", avg);
    return 0;
}

Example

expected output

average: 7.67
Hint 1

(a + b + c) / 3 is int divided by int, so the fraction is thrown away before anything is stored in avg.

Hint 2

Make one operand of the division a double, for example by dividing by 3.0.

Reference solution
#include <stdio.h>

int main(void)
{
    int a = 7;
    int b = 8;
    int c = 8;
    double avg = (a + b + c) / 3.0;
    printf("average: %.2f\n", avg);
    return 0;
}

Exercise 4 · write code

Seconds to hours, minutes, seconds

Write a function

void print_hms(int total)

that prints a non-negative number of seconds as hours, minutes and seconds in the form H:MM:SS followed by a newline. Minutes and seconds always have two digits; hours have as many as needed. Examples:

print_hms(3725)   prints  1:02:05
print_hms(59)     prints  0:00:59
print_hms(86399)  prints  23:59:59
print_hms(360000) prints  100:00:00

Use / and %. The check calls your function with several values and compares what it prints.

File: main.c

Starting point

#include <stdio.h>

void print_hms(int total)
{
    printf("%d\n", total);
}

int main(void)
{
    print_hms(3725);
    return 0;
}
Hint 1

There are 3600 seconds in an hour: total / 3600 is the number of whole hours.

Hint 2

total % 3600 is what is left after removing whole hours; divide that by 60 for minutes. total % 60 is the seconds.

Hint 3

%02d prints at least two digits, padding with a zero.

Reference solution
#include <stdio.h>

void print_hms(int total)
{
    int hours = total / 3600;
    int minutes = total % 3600 / 60;
    int seconds = total % 60;
    printf("%d:%02d:%02d\n", hours, minutes, seconds);
}

int main(void)
{
    print_hms(3725);
    return 0;
}

Exercise 5 · write code

How many buses?

A school trip has people travelers and each bus holds capacity people. Write a function

int buses_needed(int people, int capacity)

that returns how many buses are needed: 40 people with capacity 20 need 2 buses, but 41 need 3, and 0 people need 0 buses. Both arguments are non-negative and capacity is at least 1.

Plain people / capacity rounds down; you need to round up. Do it with integer arithmetic only: no if, no floating point, no <math.h>.

File: main.c

Starting point

#include <stdio.h>

int buses_needed(int people, int capacity)
{
    return people / capacity;
}

int main(void)
{
    printf("%d\n", buses_needed(41, 20));
    return 0;
}
Hint 1

Rounding up means: if there is any remainder at all, add one more bus.

Hint 2

Adding capacity - 1 before dividing pushes every non-exact case over the next multiple, but leaves exact multiples where they are.

Hint 3

return (people + capacity - 1) / capacity;

Reference solution
#include <stdio.h>

int buses_needed(int people, int capacity)
{
    return (people + capacity - 1) / capacity;
}

int main(void)
{
    printf("%d\n", buses_needed(41, 20));
    return 0;
}