C fundamentals ยท lesson 01

Hello, C

C is 50 years old and still runs your operating system, your database, your browser's rendering engine. Here's why, and here's what happens when you press compile.

in progress
8 min

Why does C still exist?

Most languages protect you from yourself. They manage memory for you, check array bounds, prevent you from reading uninitialized variables. C does none of that. It gives you direct access to memory, no abstractions between you and the hardware.

That sounds terrifying. It is, until you understand it. Then it becomes a superpower. The Linux kernel is C. SQLite is C. The Python interpreter is C. When you need something to run fast, run small, or run forever, C is still the language you reach for.

๐Ÿ’ก
What C gives you: a thin layer of abstraction over machine instructions. Every C operation maps directly to something the CPU can do. There are no hidden costs, no garbage collector pausing your program, no runtime deciding what to do with your memory.

A C program runs on bare metal

In Python, when you write x = [1, 2, 3], the runtime allocates a list object, tracks its reference count, and will free it for you. In C, you are the runtime. You decide where memory lives, how long it lives, and what happens when you're done with it.

This means more responsibility, but also more precision. A C program running on a 10-year-old embedded chip has no runtime overhead. It does exactly, and only, what you wrote.

The compilation pipeline

When you write C and run gcc hello.c -o hello, you think one thing happens. Four things actually happen.

shell
# The four stages GCC runs for you automatically: # 1. Preprocessor โ€” expands #include and #define gcc -E hello.c -o hello.i # 2. Compiler โ€” turns C into assembly gcc -S hello.i -o hello.s # 3. Assembler โ€” turns assembly into machine code (object file) gcc -c hello.s -o hello.o # 4. Linker โ€” combines object files, resolves symbols (printf, etc.) gcc hello.o -o hello

Each stage transforms the source into a lower-level representation. You don't need to run them separately โ€” gcc hello.c -o hello does all four. But understanding that they exist explains a lot of errors you'll see. An "undefined reference" error happens at stage 4: the linker couldn't find a function you called.

๐Ÿ’ก
Read the assembly: gcc -O2 -S hello.c -o hello.s writes the generated assembly to a file. You don't need to understand every line โ€” just knowing it exists demystifies what "compiling" means.

Your first C program, explained

c
#include <stdio.h> // stage 1: copy the contents of stdio.h here int main() { // the OS calls main() to start your program printf("hello\n"); // call the C standard library return 0; // tell the OS: success (non-zero = error) }

The #include <stdio.h> line is a preprocessor directive โ€” it copies the contents of stdio.h into your file before compilation. That header declares printf so the compiler knows what it expects as arguments and return type.

main() is special: the OS calls it to start your program. The return value is the exit code. Zero means success. Non-zero means failure. The shell can check this with echo $?.

โš ๏ธ
C is not forgiving. A typo that Python catches at runtime, C might silently compile into broken behavior. Always compile with warnings enabled: gcc -Wall -Wextra -o hello hello.c. Treat warnings as errors.

What comes next

The rest of this track is about what's happening beneath the surface. Variables aren't just names โ€” they're addresses. Types aren't just categories โ€” they're byte layouts. Functions aren't just calls โ€” they're memory frames pushed and popped on a stack.

Once you see C the way the machine sees it, bugs stop being mysterious. They become predictable consequences of specific rules you now know.

one-line takeaway

C is not a high-level language with extra steps โ€” it is a thin wrapper around the machine itself.