Frontend Labs
⋯

Part I — Foundations: the C way

The language itself, in order: the toolchain, then every piece a C program is made of — types, control flow, functions, arrays and pointers, strings, structs, and the preprocessor. Three checkpoint sections (pointers, strings, structs/macros) let you confirm you're solid before moving on instead of finding out three sections later. Two terminal mini-projects close out the Part before Part II adds dynamic memory on top.

1. What C is, why it still matters, and which C you're learning

1.1 The problem C solved, and still solves

C was designed by Dennis Ritchie at Bell Labs (1969–1973) to rewrite Unix in something more portable than assembly. Its entire design philosophy is one sentence: give the programmer direct, predictable control over memory and machine instructions, and trust them with it. Nothing is inserted behind your back — no garbage collector, no hidden bounds checks, no implicit allocations, no exceptions unwinding a stack you didn't ask to unwind. Every cost is visible in the source.

That's why, 50+ years later, C is still common near the bottom of the stack: Linux and many embedded kernels; important parts of Windows; language runtimes such as CPython, the JVM, V8, and Go's early runtime; SQLite; Redis; curl; OpenSSL; git's core; PostgreSQL. If a program needs to run close to the hardware, on a tiny device, or as a reference implementation other languages bind against, C is often one of the first languages considered.

1.2 What C deliberately does not have

No classes, no exceptions, no garbage collector, no built-in strings (a "string" is a convention: a pointer to char plus a '\0' terminator), no bounds checking on arrays, no function overloading (until _Generic in C11, which is a macro trick, not real overloading), no namespaces. This is not primitiveness — it's the reason a C ABI is the lingua franca every other language calls into (Python's ctypes, Rust's extern "C", Go's cgo, Java's JNI all speak C calling conventions, because nothing simpler exists to agree on).

1.3 Which C you're learning: standards and what changed

Standard Year What it added that you'll actually use
C89/C90 (ANSI C) 1989 The baseline everyone assumes: function prototypes, void*, const
C99 1999 // comments, stdint.h fixed-width types, variable-length arrays (VLAs), designated initializers, for (int i = ...), inline, _Bool, compound literals
C11 2011 <stdatomic.h>, <threads.h>, _Generic, _Static_assert, anonymous structs/unions, _Noreturn
C17 2018 Bugfix release, no new features — the safest "modern C" target today
C23 2024 nullptr, bool/true/false as keywords (no <stdbool.h> needed), constexpr, typeof/typeof_unqual, #embed, [[attributes]] ([[nodiscard]], [[maybe_unused]], [[deprecated]]), binary literals (0b1010), digit separators (1'000'000), _BitInt(N), removal of implicit-int and K&R-style function definitions

Which one should you target? C17 for anything that must compile on older distros (RHEL, embedded toolchains) or interop with a wide range of compilers. C23 when you control the toolchain — it removes several classes of footgun (nullptr vs. NULL's type ambiguity, bool finally being a real keyword) with zero downside on GCC 13+/Clang 17+. This guide defaults to C17 syntax and calls out C23 alternatives inline.

# Check what your compiler actually supports
clang --version
gcc --version
clang -std=c23 -dM -E -x c /dev/null | grep __STDC_VERSION__
# 202311L  == C23. 201710L == C17. 201112L == C11.

1.4 War story

CVE-2014-0160 — Heartbleed — was a missing bounds check in OpenSSL's TLS heartbeat extension: the code read payload_length bytes from a buffer using a length supplied by the attacker, with no check that the buffer actually contained that many bytes. One memcpy with an attacker-controlled length leaked private keys, session cookies, and passwords from millions of servers for two years before anyone noticed. The fix was three lines. The lesson that shaped an entire industry's approach to C (Part V of this guide) was: in C, the compiler will never save you from reading past a buffer — only you, your tests, your sanitizers, and your code review can.


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