16 progressive lessons · 40+ graded exercises with solutions · 3 build-it-yourself projects
No slides. No pseudocode. Every single file in this repository compiles and runs.
Most C tutorials stop at printf. The interesting part of C — the part that decides whether your
program is correct or merely lucky — starts at pointers and does not end until you understand who
owns which byte and when it gets freed.
C_educator walks that whole distance. Each lesson is a folder you can cd into, build with one
command, and break on purpose. The exercises have real solutions, not hints. The projects are small
enough to finish in an evening and large enough to teach you something.
hello.c ──► ┌──────────────┐
│ preprocessor │ ──► expanded source gcc -E
└──────────────┘
┌──────────────┐
│ compiler │ ──► assembly gcc -S
└──────────────┘
┌──────────────┐
│ assembler │ ──► object file gcc -c
└──────────────┘
┌──────────────┐
│ linker │ ──► executable gcc
└──────────────┘
Lesson 01 makes you run every one of those four stages by hand. After that you will never again wonder what "undefined reference" means.
git clone https://github.com/limonuser/C_educator.git
cd C_educator
make # build every lesson, project and solution
make lesson-09 # build just the pointers lesson
make run-09 # build it and run it
make clean # remove all binariesNo compiler yet? See docs/SETUP.md — Linux, macOS and Windows covered in three commands each.
Work top to bottom. Each lesson assumes the one above it and nothing else.
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 01 | Hello, World | The four compilation stages, main's return value, what #include really does |
hello.c |
| 02 | Variables & Types | Integer widths, overflow, float vs double, why int is not 32 bits by law |
types.c |
| 03 | Operators | Precedence, integer division traps, bit manipulation, short-circuit evaluation | operators.c |
| 04 | Control Flow | if/else chains, switch fallthrough as a feature, the dangling-else trap |
control.c |
| 05 | Loops | for/while/do-while, off-by-one errors, break vs continue, loop invariants |
loops.c |
| 06 | Functions | Prototypes, pass-by-value, scope, recursion and its stack cost | functions.c |
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 07 | Arrays | Contiguous storage, bounds are your job, multidimensional layout | arrays.c |
| 08 | Strings | char * vs char[], the NUL terminator, why strcpy is a loaded gun |
strings.c |
| 09 | Pointers | Address-of, dereference, NULL, pointer arithmetic, the type system's role |
pointers.c |
| 10 | Pointers & Arrays | Array decay, arr[i] == *(arr+i), passing arrays to functions, 2D via pointers |
decay.c |
| 11 | Dynamic Memory | malloc/calloc/realloc/free, ownership, leaks, use-after-free, growth strategy |
heap.c |
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 12 | Structs & Unions | Composite types, padding and alignment, ->, unions as tagged variants |
structs.c |
| 13 | File I/O | fopen/fclose, text vs binary, fgets over gets, error-checking discipline |
fileio.c |
| 14 | Preprocessor & Headers | Macros and their pitfalls, include guards, multi-file programs, static linkage |
main.c |
| 15 | Function Pointers | Callbacks, qsort with your own comparator, dispatch tables |
callbacks.c |
| 16 | Linked Structures | Singly linked list, stack, queue — built from scratch, freed correctly | list.c |
Forty problems, sorted by difficulty. Every one ships with a reference solution — but the solution is a separate file, so you have to choose to look.
| Tier | Problems | Sample |
|---|---|---|
| Beginner | 15 | FizzBuzz, prime sieve, temperature table, digit sum, palindrome check |
| Intermediate | 15 | Matrix multiply, binary search, string tokenizer, bit counting, Caesar cipher |
| Advanced | 10 | Dynamic array, hash table, memory pool, bitset, matrix determinant |
cd practice/beginner
make # build all fifteen solutions
./01_fizzbuzz # run one
make run # or run every solution in turnThree programs worth putting on a résumé — each one real, self-contained C.
| Project | What it is | Concepts pulled in |
|---|---|---|
| 01 — Calculator | An RPN expression evaluator with its own stack | arrays, parsing, error handling |
| 02 — Todo CLI | A task manager that persists to disk | dynamic memory, structs, file I/O |
| 03 — Word Frequency | A hash-table word counter with sorted output | hashing, qsort, dynamic growth |
C_educator/
├── lessons/ 16 lessons, each a folder you can cd into
│ ├── 01-hello-world/ README.md + hello.c
│ ├── ...
│ └── 14-preprocessor/ README.md + main.c + mathutils.{c,h}
├── practice/ 40 problems with reference solutions
│ ├── beginner/ 15 · loops, arrays, strings
│ ├── intermediate/ 15 · algorithms, bits, parsing
│ └── advanced/ 10 · everything that calls malloc
├── projects/ 3 complete programs
│ ├── 01-calculator/ RPN evaluator calc.c
│ ├── 02-todo-cli/ persistent tasks todo.c
│ └── 03-word-frequency/ hash-table counter wordfreq.c
├── docs/ setup · cheatsheet · UB traps · style · roadmap
├── .github/workflows/ CI: gcc + clang, sanitizers, valgrind
└── Makefile builds and runs all 59 programs
Every .c file here is held to the same standard, and CI enforces it on every push:
gcc -std=c17 -Wall -Wextra -Wpedantic -Werror file.c -o file- Zero warnings. Not "a few harmless ones" — zero.
-Werrorturns a warning into a build failure. - Zero leaks. Anything that
mallocs alsofrees, and the comment says who owns it. - Every return value checked.
malloc,fopen,fgets— all of them. - Runs, not just compiles. CI executes each binary and fails on a non-zero exit.
make check builds all 59 programs and runs every one of them. The expected last
line is all 59 programs exited 0.
If you find a file that violates any of these, that is a bug — please open an issue.
| Document | Contents |
|---|---|
| docs/SETUP.md | Getting a compiler on Linux, macOS and Windows |
| docs/CHEATSHEET.md | Format specifiers, operator precedence, <string.h> at a glance |
| docs/UB_TRAPS.md | Fourteen ways to invoke undefined behaviour, and how to avoid each |
| docs/STYLE_GUIDE.md | The conventions every file here follows |
| docs/ROADMAP.md | Where this curriculum goes next |
- Read the lesson README first. It frames the problem before showing the code.
- Type the example out. Do not copy-paste. Muscle memory is part of the point.
- Break it deliberately. Remove an
&, drop thefree, index one past the end. Watch what the compiler says — and what it does not say. - Do the exercises before reading the solutions. A solution you read is worth roughly a tenth of a solution you wrote.
- Move on before you feel ready. C rewards returning to a topic more than grinding it.
New exercises, clearer explanations and bug reports are all welcome — see CONTRIBUTING.md.
MIT — use it, fork it, teach with it.