01 — pointers & arrays
A pointer is an address you can do arithmetic on; arrays decay to pointers. Power, and fifty years of bugs.
int a[4] = {10, 20, 30, 40};
int *p = a; /* a == &a[0] */
printf("%d\n", *(p + 2)); /* 30 */§ 02 — Eras · 1972
portable assembly — the substrate of everything
Left: era context, features, and the 101 hello. Right: 3 core concepts, 3 deeper 201 lessons, the factorial specimen, and where C lives today — each column scrolls on its own.
Operating systems were written in assembly that could never move between machines. C made Unix — and portability — possible.
It trusts the programmer absolutely; undefined behavior and buffer overflows became the security industry’s substrate.
Session one is the smallest program that still counts as speaking C: what every tutorial prints first, in the dialect that mattered when the language arrived (1972).
#include <stdio.h>
int main(void) {
printf("Hello, world\n");
}101First contact — hello world and 3 ideas that define how C reads. 201 — 3 lessons on mechanisms you meet after the factorial specimen: the sharp edges practitioners actually use.
3 fragments, each naming a habit of mind in C. Read the note, then the code — together they are the 101 in one sitting.
01 — pointers & arrays
A pointer is an address you can do arithmetic on; arrays decay to pointers. Power, and fifty years of bugs.
int a[4] = {10, 20, 30, 40};
int *p = a; /* a == &a[0] */
printf("%d\n", *(p + 2)); /* 30 */02 — structs
Data grouped, laid out exactly as you say — C trusts you completely.
struct ship { char *name; int tons; };
struct ship s = {"Perch", 24};
s.tons += 10;03 — manual memory
You allocate, you free. Every leak and use-after-free is yours — and Rust exists because of this line.
char *buf = malloc(64);
if (buf == NULL) return 1;
free(buf);The 201 assumes you have read the 101 and the specimen. Each lesson is one mechanism — what it is for, how it looks in code, and the trap or payoff that makes C distinctive at scale.
A function’s address is an ordinary value, which is how C does callbacks, dispatch tables and plug-in architectures — the pattern behind every C API ever written.
typedef int (*cmp_fn)(const void *, const void *);
int sort (void *a, void *b, cmp_fn cmp);Reading uninitialized memory, overflowing a signed int, or aliasing a pointer wrongly all mean the program may do anything. Compilers exploit this, and it keeps biting.
int x = INT_MAX;
x += 1; /* undefined: signed overflow */Masks, shifts and unions are the portable way to work with bytes and words — and the usual source of bugs when a file crosses architectures.
uint32_t swap32 (uint32_t v) {
return ((v & 0xffu) << 24) | ((v & 0xff00u) << 8)
| ((v >> 8) & 0xff00u) | ((v >> 24) & 0xffu);
}The struct and the pointer: memory you can see, types light enough to carry. Unix rewritten in C became the first portable operating system — and C became the substrate.
struct ship { char *name; int tons; };
struct ship s = { "Perch", 24 };
struct ship *p = &s;
p->tons += 10;runcc fact.c -o fact && ./fact
Unix, Linux, Windows kernels, your phone’s firmware, Python’s interpreter, and the machine you are reading this on.