Code Katas
Fluency drills. Repeat each kata until you can reach the completion bar without looking anything up.
Compile every kata with:
gcc -Wall -Wextra -std=c11 -o kata kata.c
Add -fsanitize=address,undefined when you have it available. It catches most buffer and UB bugs on the spot.
Kata 1: strlen from scratch
Time limit: 10 minutes
Goal: C string walking, const discipline
Write size_t my_strlen(const char *s); without using <string.h>. Add tests for empty, one-character, and multi-character strings.
Repeat until: you can write and test it in under 5 minutes.
Kata 2: strdup from scratch
Time limit: 15 minutes
Goal: allocation, copy, null-termination discipline
Write char *my_strdup(const char *s); that returns a fresh heap copy the caller must free. Handle NULL input by returning NULL. Match the POSIX contract.
Repeat until: you can write the correct 12-15 line version and explain every error path.
Kata 3: Dynamic integer array
Time limit: 25 minutes
Goal: struct design, growth strategy, ownership
Design struct ivec { int *data; size_t len; size_t cap; }; with:
ivec *ivec_create(void);void ivec_destroy(ivec *v);int ivec_push(ivec *v, int x);(grows by doubling whenlen == cap)int ivec_get(const ivec *v, size_t i, int *out);size_t ivec_len(const ivec *v);
Write a small test that pushes 1000 integers and reads them back.
Repeat until: you can implement all five functions with no memory leaks (verify with valgrind or AddressSanitizer).
Kata 4: Mini grep
Time limit: 30 minutes
Goal: line-oriented I/O with fgets, substring search, argv handling
Build mygrep PATTERN that reads stdin line by line and prints each line containing PATTERN (use strstr, no regex). Print errors to stderr. Exit 0 if any match, 1 if none, 2 on usage error.
cat file.txt | ./mygrep hello
Repeat until: you can implement it with correct exit codes in under 20 minutes and your fgets line buffer never overflows.
Kata 5: Bounded ring buffer
Time limit: 30 minutes
Goal: struct invariants, modular arithmetic, assertions
Design a fixed-capacity byte ring buffer:
int ring_init(Ring *r, uint8_t *storage, size_t cap);int ring_push(Ring *r, uint8_t byte);- returns 0 on success, -1 when fullint ring_pop(Ring *r, uint8_t *out);- returns 0 on success, -1 when emptysize_t ring_len(const Ring *r);
Use assert(r && r->cap > 0) where useful. The caller owns storage; Ring is just indices.
Repeat until: you can state the invariant linking head, tail, cap, and len in one sentence before writing any code.
Kata 6: Multi-file project with a Makefile
Time limit: 30 minutes
Goal: .h/.c split, Makefile, clean build
Take Kata 3 (ivec) and split it into ivec.c, ivec.h, main.c with a Makefile:
makebuildsappmake cleanremovesappand*.omake debugsetsCFLAGS += -g -O0 -fsanitize=address,undefined- The
MakefileusesCC,CFLAGS,$@,$^,$<, and.PHONY
Repeat until: you can write the Makefile from memory.
Kata 7: Trim and tokenize
Time limit: 20 minutes
Goal: C string manipulation without over-writing the buffer
Write char *trim(char *s); that removes leading and trailing whitespace in place and returns a pointer into the same buffer. Then write int tokenize(char *line, char **out, int max); that splits on spaces using strtok and fills out, returning the token count.
Repeat until: you can handle pathological inputs (all whitespace, empty, no whitespace, trailing whitespace only) correctly on the first try.
Kata 8: Safe sprintf wrapper
Time limit: 20 minutes
Goal: snprintf return-value discipline
Write int fmt_append(char *buf, size_t cap, size_t *len, const char *fmt, ...); that appends formatted text to buf, updates *len, and returns 0 on success, -1 on truncation. Use vsnprintf and handle the "truncated" case (return >= remaining).
Repeat until: your wrapper never overflows buf even when callers pass huge format output.
Completion Standard
- Every kata compiles clean under
-Wall -Wextra -std=c11. - No leaks or UB under
-fsanitize=address,undefined(where available). - You can explain every line of each kata in plain English.
- You can recreate any kata from a blank file in under the time limit.