Week 11 · Phase 2 — The Ancestry
The most-written program in history, dissected line by line.
Photo · atelierbyvineeth / Unsplash
It is 1978. Brian Kernighan and Dennis Ritchie are sitting in a Bell Labs office, finishing the manuscript of The C Programming Language. They need an example. Something tiny, that prints something to the screen, that introduces the rituals you'll perform in every C program you ever write.
They write five lines. The program prints the words "hello, world". Forty-seven years later it is, very probably, the most-written, most-translated, most-imitated program ever produced. It is the universal "I have a working compiler" handshake. It is the first thing a programming language teaches.
And it is a perfect dissection target — because every word in it leads somewhere. Today we read it slowly and look at every line.
#include <stdio.h>
int main(void) {
printf("hello, world\n");
return 0;
}
Five lines. Save this as hello.c, run cc hello.c -o hello && ./hello, and you'll see:
Now we go through it phrase by phrase.
This isn't really C. The # means "this is a preprocessor directive", which runs before the compiler proper. The preprocessor finds a file called stdio.h (the "standard I/O header") and pastes its entire contents into your file at this exact spot.
That file declares the existence of printf, among others. Without it, the compiler would have no idea what printf means when you call it on line 4. Headers are how C does "I want to use this thing that lives somewhere else". You'll see dozens of these in any real program.
You're declaring a function called main. Every C program needs exactly one of these — it's the entry point the operating system calls when your program starts. The OS doesn't know about any of your other functions; it knows about main, and it expects to find one.
int says the function returns an integer when it finishes. By convention, 0 means "success" and any non-zero value means "something went wrong". void in the parentheses means "this function takes no arguments". (We'll see int argc, char *argv[] later — that's how command-line arguments arrive.)
An opening curly brace. C marks the start of a block of code with { and the end with }. Everything between them is the function's body — the actual things to do. Indentation is purely cosmetic; the braces are what the compiler reads.
A function call. printf means "print formatted". You're passing it a single string argument. That string contains the literal characters hello, world followed by \n — a special two-character escape that becomes newline when the compiler converts the string. Without the \n the cursor would stay on the same line as the output.
The semicolon at the end is mandatory. C uses it to mark the end of every statement. Forget it and the compiler will give you a famously confusing error message that points to the next line.
The function returns the value 0 back to whoever called it — in this case, the OS. The OS interprets 0 as "everything went fine". Shell scripts, build systems, CI pipelines all use this convention: 0 = success, anything else = failure. If you forget the return in main, modern C compilers will silently insert return 0 for you. Older ones (and other functions) will not.
End of function. End of program. The OS sees the return value, cleans up, and your terminal goes back to the prompt.
Photo · Aaron Burden / Unsplash
Every word counts. Five lines, one program, half a dozen ideas hiding in plain sight.
You typed ./hello. Here's the sequence, end to end, in less than a millisecond:
./hello. The kernel checks it's an executable, reads the first few bytes (the "magic number") to confirm, and prepares a fresh process._start._start sets a few things up (initialises globals, finds the command-line arguments) and then calls main.main calls printf. printf looks at its argument, identifies that no special % placeholders need substituting, and asks the kernel — via the write syscall — to write 13 bytes to file descriptor 1 (standard output).printf returns. main hits return 0. _start takes that return value and calls the exit syscall with it. The kernel cleans up the process. The shell prints a fresh prompt.Six steps. Hundreds of thousands of CPU cycles. A trip from your shell to the kernel, into a C runtime, through your code, back through the runtime, back to the kernel, back to the shell. All of that, every time. And you don't see any of it — you just see hello, world.
Because it touches every part of the machinery you'll ever need:
Six fundamental concepts in five lines, none of them strictly necessary for printing "hello, world" — you could imagine a language where print "hello" was the entire program. C made the deliberate decision to expose the scaffolding from day one. You learn the rituals before you do anything interesting, and that pays off the rest of your career.
Once you have the program working, deliberately break it. There is no faster way to learn what each piece is doing than to remove it and see what the compiler shouts.
#include <stdio.h>. Compile. The compiler will warn that printf is "implicitly declared" — it doesn't know what printf looks like and will fall back to a guess.int main to void main. Some compilers accept it. Others reject it. The standard requires int.\n. Run again. Notice the cursor sits at the end of world instead of moving down — the prompt prints right there."hello, world" to "hello, %s" and add a second argument: printf("hello, %s\n", "World");. Welcome to format strings; we'll come back to those.Every CUDA file you ever read starts with #include <cuda_runtime.h>. Every PyTorch C++ extension starts with #include <torch/extension.h>. Every llama.cpp source file you'll ever browse begins with this same ritual. main, headers, return codes — these aren't C trivia. They are the entry point of every native program in the AI stack.
Writing your own hello.c matters because, surprisingly often, when something goes wrong in a deep AI install, you'll find yourself fixing a 30-year-old C build error two libraries down. Knowing what #include means, knowing what main is, knowing what a "return value of 1" really represents — that is suddenly your problem. And then it stops being mysterious.
Every C program ever written has the same skeleton. Once you can see it, you can see it everywhere.
If you set up a compiler last week, this is your moment. If not, see Week 9's "try it yourself" first.
hello.c.cc hello.c -o hello. There should be no output. A successful build is a quiet build../hello. You should see hello, world.echo $? (macOS/Linux) or echo %ERRORLEVEL% (Windows). You'll see 0 — that is the return value from main, which is now sitting in the shell's "exit code of the last command" variable.cc -S hello.c to dump the assembly. Open hello.s. Find the printf call. You can see the chef preparing the call exactly as Week 4 described.Welcome. You have officially run a C program.
Now we make the program actually do something. Hello world has no inputs, no logic, no state. From next week we start working with values — and to do that, we need somewhere to put them.
Week 12 is Variables & Memory — telling the RAM how much space you need, why int and char aren't the same number of bytes, and what your computer is doing under the hood when you say int x = 5;.
All photos are free under the Unsplash license. Hello sign · atelierbyvineeth · Pen · Aaron Burden.