Beginner

Operators in C

A practical tour of arithmetic, comparison, logical, bitwise, assignment, and other operators in C with runnable Docker examples

Operators are the verbs of C. They combine values and variables into expressions that compute, compare, and transform data. As a procedural, statically-typed language sitting close to the hardware, C exposes a particularly rich set of operators — including bitwise operators that map almost directly onto CPU instructions, and pointer arithmetic that other languages quietly hide.

C’s operators are largely inherited by C++, Java, C#, Go, and JavaScript, so understanding them here pays dividends across the entire C-family of languages. C’s weakly-typed nature also means operators sometimes behave in surprising ways: integer division truncates, signed overflow is undefined, and a non-zero int is “true.” These behaviors are not bugs — they reflect the language’s preference for letting the machine speak directly.

In this tutorial you will work through arithmetic, comparison, logical, bitwise, compound assignment, increment/decrement, the ternary conditional, and the sizeof operator, with a quick look at operator precedence.

A Comprehensive Operators Example

The program below exercises each major category of C operator and prints the result so you can verify the behavior yourself.

Create a file named operators.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <stdio.h>

int main(void) {
    /* Arithmetic operators on integers */
    int a = 17;
    int b = 5;
    printf("a + b = %d\n", a + b);
    printf("a - b = %d\n", a - b);
    printf("a * b = %d\n", a * b);
    printf("a / b = %d (integer division truncates)\n", a / b);
    printf("a %% b = %d (modulus / remainder)\n", a % b);

    /* Floating-point division behaves differently */
    double x = 17.0;
    double y = 5.0;
    printf("x / y = %.2f\n", x / y);

    /* Comparison operators yield 1 (true) or 0 (false) */
    printf("a == b: %d\n", a == b);
    printf("a != b: %d\n", a != b);
    printf("a >  b: %d\n", a > b);
    printf("a <  b: %d\n", a < b);

    /* Logical operators: any non-zero value is true */
    int t = 1, f = 0;
    printf("t && f: %d\n", t && f);
    printf("t || f: %d\n", t || f);
    printf("!t    : %d\n", !t);

    /* Bitwise operators work on each bit of an integer */
    unsigned int m = 0xF0;  /* 11110000 */
    unsigned int n = 0x0F;  /* 00001111 */
    printf("m & n  = %u\n", m & n);
    printf("m | n  = %u\n", m | n);
    printf("m ^ n  = %u\n", m ^ n);
    printf("n << 4 = %u\n", n << 4);
    printf("m >> 4 = %u\n", m >> 4);

    /* Compound assignment operators */
    int c = 10;
    c += 5; printf("c += 5  -> %d\n", c);
    c -= 3; printf("c -= 3  -> %d\n", c);
    c *= 2; printf("c *= 2  -> %d\n", c);
    c /= 4; printf("c /= 4  -> %d\n", c);

    /* Increment / decrement: postfix vs prefix */
    int i = 5;
    printf("i++ returns %d ", i++);
    printf("(i is now %d)\n", i);
    printf("++i returns %d\n", ++i);

    /* Ternary conditional operator */
    int max = (a > b) ? a : b;
    printf("max(a, b) = %d\n", max);

    /* Operator precedence: * binds tighter than + */
    printf("2 + 3 * 4   = %d\n", 2 + 3 * 4);
    printf("(2 + 3) * 4 = %d\n", (2 + 3) * 4);

    /* sizeof is a compile-time operator, not a function */
    printf("sizeof(int)    = %zu bytes\n", sizeof(int));
    printf("sizeof(double) = %zu bytes\n", sizeof(double));

    return 0;
}

Running with Docker

1
2
3
4
5
6
# Pull the official GCC image
docker pull gcc:14

# Compile and run the operators example
docker run --rm -v $(pwd):/app -w /app gcc:14 \
    sh -c "gcc -o operators operators.c && ./operators"

Expected Output

a + b = 22
a - b = 12
a * b = 85
a / b = 3 (integer division truncates)
a % b = 2 (modulus / remainder)
x / y = 3.40
a == b: 0
a != b: 1
a >  b: 1
a <  b: 0
t && f: 0
t || f: 1
!t    : 0
m & n  = 0
m | n  = 255
m ^ n  = 255
n << 4 = 240
m >> 4 = 15
c += 5  -> 15
c -= 3  -> 12
c *= 2  -> 24
c /= 4  -> 6
i++ returns 5 (i is now 6)
++i returns 7
max(a, b) = 17
2 + 3 * 4   = 14
(2 + 3) * 4 = 20
sizeof(int)    = 4 bytes
sizeof(double) = 8 bytes

Pointers and the Address-of Operator

C exposes two operators that other languages typically hide: & (address-of) and * (dereference). These are operators, not just syntax for declarations, and they let you read and write through pointers.

Create a file named pointer_ops.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

int main(void) {
    int value = 42;
    int *p = &value;       /* & takes the address of value */

    printf("value          = %d\n", value);
    printf("*p (deref)     = %d\n", *p);

    *p = 100;              /* write through the pointer */
    printf("value after *p = 100 -> %d\n", value);

    /* Pointer arithmetic moves by the size of the pointed-to type */
    int nums[3] = {10, 20, 30};
    int *q = nums;
    printf("q[0] = %d, *(q+1) = %d, *(q+2) = %d\n",
           q[0], *(q + 1), *(q + 2));

    return 0;
}

Run it the same way:

1
2
docker run --rm -v $(pwd):/app -w /app gcc:14 \
    sh -c "gcc -o pointer_ops pointer_ops.c && ./pointer_ops"

Expected Output

value          = 42
*p (deref)     = 42
value after *p = 100 -> 100
q[0] = 10, *(q+1) = 20, *(q+2) = 30

A Note on Integer Division and Overflow

C’s weak typing shows up most often around arithmetic:

  • Integer division truncates toward zero. 17 / 5 is 3, not 3.4. If either operand is a floating-point type, the result is floating-point.
  • Signed integer overflow is undefined behavior — the compiler is allowed to assume it cannot happen. Use unsigned types for well-defined wrap-around arithmetic.
  • Mixing signed and unsigned in a comparison can produce surprising results because the signed value is converted to unsigned. Prefer matching types.

Key Concepts

  • Integer division truncates toward zero; use a floating-point operand to get a fractional result.
  • Comparison and logical operators produce int values of 0 or 1 — C has no dedicated boolean type until _Bool/stdbool.h (introduced in C99).
  • Bitwise operators (&, |, ^, ~, <<, >>) operate on individual bits and map directly onto common CPU instructions, making them cheap and fast.
  • Compound assignment (+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=) is shorthand that evaluates the left-hand side only once.
  • Postfix i++ returns the old value, then increments; prefix ++i increments first, then returns the new value.
  • sizeof is a compile-time operator, not a function — it returns a size_t and never evaluates its operand at runtime.
  • & and * are operators, giving you the address of an object and the value at an address respectively — pointer arithmetic respects the pointed-to type’s size.
  • Operator precedence and associativity matter: when in doubt, add parentheses — the compiler will not penalize you for clarity.

Running Today

All examples can be run using Docker:

docker pull gcc:14
Last updated:

Comments

Loading comments...

Leave a Comment

2000 characters remaining