Frontend Labs
⋯

How to use this guide

  • Beginner (Part I): the toolchain, types, control flow, functions, arrays/pointers, strings, structs, the preprocessor. The three trickiest sections (pointers, strings, structs/macros) each end with a 🔎 Checkpoint — a handful of predict-the-output/spot-the-bug/write-it- yourself questions with collapsible answers, so you can confirm you're ready before moving on instead of finding out three sections later. Part I then ends with two terminal mini-projects.
  • Intermediate (Part II): dynamic memory, multi-file programs, file I/O, debugging/sanitizers, a minimal test harness. Ends with a vector library + CSV parser, and a JSON parser.
  • Advanced (Part III): data structures from scratch, intrusive containers (the Linux-kernel trick), threads and atomics, sockets, _Generic. Ends with a thread pool and a multi-client chat server.
  • Real-world builds (Part IV): production CLIs, a text editor, a Unix shell, a bump allocator.
  • Security-focused C (Part V): undefined behavior, safe string handling, hardening flags, fuzzing, static analysis.
  • Open-source walkthroughs (Part VI): SQLite, Redis, curl, cJSON/jq, Linux-kernel-style intrusive lists, uthash/klib — the same concepts you just learned, read out of real, battle-tested codebases.
  • Expert (Part VII): the C11 memory model, writing a real allocator, ABI and API design, cross-compilation and build systems, profiling. Ends with a capstone: a mini Redis-like key-value store with a wire protocol.
  • Writing safe, leak-free C (Part VIII): every memory-leak shape you'll actually hit in production (not just "forgot to free"), a defensive- programming checklist, and a bad-code/good-code catalog of the mistakes real C programmers make — read this even if you skip everything else.
  • Hardware & driver programming (Part IX): the senior-engineer layer — talking to real hardware from user space (GPIO/I2C/SPI/serial), memory-mapped registers and volatile, writing and loading your first Linux kernel module, character device drivers, interrupts, and the concurrency rules that change once your code runs in kernel context. Ends with a capstone: one sensor, driven two ways.
  • Signals, non-local jumps, and mapped files (Part X): the three general-purpose systems topics every "expert C" checklist expects that the rest of this guide only mentions in passing — signal/sigaction, setjmp/longjmp, and mmap for real files (as opposed to Part VII's anonymous allocator mapping or Part IX's hardware-register mapping).
  • Appendices: C17→C23 feature diff, the UB cheat sheet, compiler flags, glossary, further reading.

Conventions:

  • Code targets C17 (ISO/IEC 9899:2018) as the safe default and calls out C23 (ISO/IEC 9899:2024) features explicitly where used (nullptr, typeof, #embed, constexpr, [[attributes]], _BitInt, binary literals). Toolchain used throughout: recent Clang/Apple Clang and GCC releases. Newer compilers accept -std=c23; some older releases use -std=c2x for the same in-progress standard mode. Where a snippet needs something GCC/Clang-specific beyond the standard (a builtin, an attribute), it's labeled.
  • "Real-world example" boxes ground a concept in something you'll actually write. "War story" boxes describe a real class of incident — the kind that shows up in CVE databases and postmortems — caused by getting that concept wrong.
  • Every code block is meant to compile. Where a snippet is wrong on purpose (to demonstrate a bug or UB), it's labeled // BUG or // UB — do not do this.

Generated from real-life-c-guide.md · 8 Oct 2026 16:36