Phase 1 — C Language Deep (Month 2: August 2026)¶
Phase 0 got your tools sharp. Phase 1 goes back to the language itself and actually teaches you the parts you didn’t learn in college — or learned and forgot, or learned wrong. You’ll come out of this month knowing why signed overflow is undefined but unsigned wraparound is defined, why strncpy is a foot-cannon, why struct { int a; char b; int c; } is not 9 bytes, and what the compiler is legally allowed to do to your code when you write *(int*)&f where f is a float.
This is a reading-heavy month. Two books are open on your desk in parallel: Beej’s Guide to C (free, ~200 pages, current through C23) as your fast reference, and K.N. King’s C Programming: A Modern Approach (830 pages, C99-era) for depth on exercises. Modern C by Jens Gustedt is the tiebreaker when you want the standards-lawyer view. Do not read them front to back; read them by topic as you work through the files here.
M2 Target¶
By the end of August 2026 you should:
Be able to explain integer promotion, sign extension, and why
size_t i = 0; i--;gives you a very large number, at whiteboard level.Write a safe C string routine (dynamic string type, append/split/free) from scratch that passes ASan.
Read a struct definition and correctly predict
sizeof,alignof, and the padding layout without compiling it — confirming withoffsetofand a small dump.Recognize at least 10 undefined-behavior patterns on sight and name the standard clause or the class (aliasing / lifetime / sequence / integer / pointer).
Ship a small bytecode VM (see projects.md) that showcases struct+union+switch dispatch.
The C11 / C17 / C23 Story¶
One of the things nobody tells you when you come back to C after a decade: the language moved. You were taught C89 or C99. The current world is C17 (a bugfix of C11, published 2018) shipping as the default in most compilers, with C23 (ISO/IEC 9899:2024, published 2024) rolling out in GCC 14+ and Clang 18+. What actually changed that matters for you:
C99 brought
//comments, mixed declarations,<stdint.h>,<inttypes.h>,<stdbool.h>, VLAs, designated initializers, compound literals. If you write “modern C” you write C99 at minimum.C11 brought
<threads.h>,<stdatomic.h>,_Generic,_Static_assert, anonymous struct/union members, aligned_alloc.C17 is C11 with defect reports fixed. No new features. When people say “target C17” they mean “C11 plus errata.”
C23 brought
bool/true/false/nullptr/static_assertas keywords (finally), binary literals (0b1010),typeof,constexpr,[[nodiscard]]and other[[attributes]],_BitInt(N),#embed, better UTF-8 support, mandated two’s-complement signed integers.
Practical rule for this roadmap: target C17. That’s what your compilers default to and it’s portable everywhere. Read 06_c_standards_landscape.md to know which C23 goodies you can opportunistically use if a compiler check passes.
Exit Criteria (all must be true)¶
You can implement, from scratch, without looking anything up: a length-prefixed dynamic string type with
str_new,str_append,str_free, and astrsplitthat returns achar **(or a small struct wrapper). ASan-clean.You can, on a whiteboard, draw the memory layout of
struct { char a; int b; char c; double d; };on a typical 64-bit target, with padding bytes marked, and predictsizeofcorrectly.You can name and give a two-sentence explanation of each of these 12 UB classes: signed integer overflow, strict aliasing violation, uninitialized read, out-of-bounds access, null dereference, use-after-free, double free, misaligned access, VLA size overflow / non-positive size, sequence-point violation, unspecified evaluation order (in a way that changes results),
INT_MIN / -1.You have shipped Project 2 (the bytecode VM), <phone_number_or_numberic_id_or_random_id_20> lines, running a hand-assembled program end to end.
You are comfortable reaching for
size_tfor indexing,ptrdiff_tfor pointer differences,int32_t/uint64_tfor exact widths, and you understand whyintis not automatically fine everywhere.
What Most People Get Wrong About Phase 1¶
They treat C as “like Python but with pointers.” It isn’t. C has an abstract machine the standard specifies, and your compiler is allowed to assume you never invoke undefined behavior. When you do, it can (and modern optimizers routinely will) delete branches, reorder code, or produce output that looks nothing like your source. The single biggest mental shift this month: you are not writing instructions for your CPU. You are writing a program for the C abstract machine, which the compiler translates to your CPU under the assumption that you never break the rules. Signed overflow being UB is not a bug; it’s what lets the compiler prove that i + 1 > i and hoist your loop counter into a register.
The second most common failure: skipping the UB catalog because “my code works.” Your code works today, on this compiler, at this optimization level. Flip -O0 to -O2 and watch a UB-riddled function suddenly return the wrong value in production. Read 05_undefined_behavior_catalog.md twice this month, not once.
Files in This Phase¶
# |
File |
What it covers |
|---|---|---|
1 |
signed/unsigned, size_t, promotion, stdint.h |
|
2 |
array decay, |
|
3 |
padding, alignment, offsetof, FAMs, tagged unions |
|
4 |
include semantics, guards, do-while(0), X-macros |
|
5 |
the 12 UBs an ML engineer will actually hit |
|
6 |
C89/99/11/17/23 — what changed, what to use |
|
7 |
dynamic string type + tiny bytecode VM |
Time Budget (10-15 h/week × 4 weeks = 40-60 h)¶
Week 1: files 01 + 02, and start Project 1 (dynamic string)
Week 2: files 03 + 04, finish Project 1
Week 3: file 05 (the UB catalog is a full week’s read + exercises)
Week 4: file 06 + Project 2 (bytecode VM)
Return to ../01_refresher_and_toolchain/README.md · Next: 01_types_and_integer_promotion.md