basics · beginner · ~20 min

Caesar cipher (educational toy)

Modular arithmetic, character classification, and the negative-modulo trap.

Challenge

Implement void caesar_shift(char *s, int k) that shifts each ASCII letter in s by k positions, wrapping within its case ('A'..'Z' or 'a'..'z'). Non-letter characters are left unchanged.

  • k may be any int (positive, negative, or large). Normalise modulo 26.
  • The function mutates s in place.

Examples

caesar_shift("abc", 1)      -> "bcd"
caesar_shift("xyz", 3)      -> "abc"
caesar_shift("Hello, X!", 1)-> "Ifmmp, Y!"
caesar_shift("abc", -1)     -> "zab"
caesar_shift("abc", 27)     -> "bcd"   // 27 % 26 == 1

Edge cases

  • Mixed case must be preserved.
  • Negative shift values must wrap correctly ((c - 'a' + (k % 26 + 26)) % 26).
  • Non-letter bytes (digits, punctuation, whitespace) are passed through verbatim.

Why this matters

Many real bugs come from forgetting that % in C can return negative values for negative operands — adding + 26 (or whatever the modulus is) before the final % fixes that.

Why this matters

The Caesar cipher is the Hello World of cryptography — never use it for real secrets, but implementing it teaches modular arithmetic over a small alphabet and is great practice with isupper/islower. (Real applications use vetted libraries like libsodium.)

Input format

Null-terminated mutable string + shift amount.

Output format

s is mutated in place.

Constraints

ASCII-only. No allocations. Single pass.

Starter code

void caesar_shift(char *s, int k) { /* TODO */ }

Common mistakes

Forgetting that (-1) % 26 == -1 in C — the result must be normalised by adding 26 before the final %. Treating uppercase and lowercase with one branch (causes case-swapping bugs).

Edge cases to handle

Empty string; k == 0; k negative; k > 26.

Complexity

O(strlen(s)).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.