Why hello-world earns its keep
You have spent this rung building a mental map: a computer is a stored-program machine that fetches and runs instructions, high-level and low-level languages sit at different heights of the abstraction stack, and compiled and interpreted languages take two different roads from your text to a running process. Now we cash all of that in. The point of a first program is not that it does something impressive — it does almost nothing — but that running it exercises the entire path from source to silicon, so every piece you have read about becomes a thing you can see.
A first program in C is traditionally a hello-world program: print one line, then stop. It is the smallest honest thing a language can do that still touches the outside world. To print, your program must hand a string to the C standard library, which eventually asks the kernel to write bytes to your terminal — so even this tiny program quietly crosses from your code, through a library, down to the operating system and back. We will not pretend that crossing is trivial, but for now you only need to watch it happen.
The seven lines, and what each one is for
Here is the whole program. Type it into a file named hello.c exactly as shown — C cares about the details. Notice it is genuinely short, but nothing here is decoration: every line earns its place, and we will walk through them one at a time.
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}The first line, #include <stdio.h>, is a preprocessor instruction that pastes in the declarations for standard input/output, including printf(); without it the compiler would not know what printf() is. The line int main(void) defines the program's entry point — the one function the system calls to start you off, named main; the int says it hands back an integer, and the void says it takes no arguments here (later you will meet the argc and argv version that receives command-line arguments). Inside, printf() asks the library to print the text, where \n is a single newline character, not a backslash followed by an n.
The last line, return 0, is the most underrated. Returning from main hands a small integer back to whoever launched the program; by long convention 0 means success and any nonzero value means a kind of failure. That integer becomes the program's exit status, which the shell remembers and tools like make read to decide whether to keep going. So this program does two visible things: it prints a line, and it reports "all went well."
Compiling: from text to an executable
Your hello.c is just text — the CPU cannot run text. Because C is a compiled language, you must first translate it into machine code using a compiler, the headline tool of the toolchain. On most systems the command is gcc or clang. In the same folder as your file, run "gcc -Wall -o hello hello.c" to build, then "./hello" to run it; you should see the line Hello, world! appear. Finally, run "echo $?" and the shell prints 0.
Look at that command piece by piece. The flag -o hello names the output file hello; -Wall asks the compiler to turn on common warnings, which you always want. The result is an executable: a real file full of machine instructions that the operating system can load and run directly. You run it with ./hello — the ./ says "the program right here in this directory" — and it prints its line. The command echo $? shows the exit status of the last program, and there is your 0 from return 0, having survived the whole journey out of the program and into the shell.
What the compiler quietly did
That single gcc command hid several distinct stages of the toolchain, each doing a real job. It is worth knowing they exist, even before you study them in depth, because when a build fails the error usually names the stage that choked.
- The preprocessor handles your #include line, pasting the contents of stdio.h in so that printf() is declared before you call it.
- The compiler proper translates your C into low-level instructions for your specific CPU, the first step of turning source into machine code.
- The assembler turns those instructions into raw binary machine code, packaged in an object file.
- The linker stitches your code together with the library code for printf() and a small startup routine, producing the final executable named hello.
When you run ./hello, none of those tools are involved any more — the work is already done and frozen into the file. The operating system loads the executable into memory and jumps to its startup code, which sets things up and then calls your main(). This is exactly the compiled road you read about: pay the translation cost once, up front, and every later run is just the CPU executing finished instructions, with no interpreter standing in between.
The loop you will live in
What you just did has a name: the edit-compile-run loop. You edit the source, compile it, run the result, look at what happened, and go around again. Almost all of your time as a systems programmer is spent inside this loop, tightening it until the gap between "I had an idea" and "I saw it run" is as short as possible. Try it now: change the text inside the quotes, recompile with the same gcc line, and run again. The new words appear because you changed the source and re-translated it — the old executable never changes itself.
One honest caveat before you celebrate. C will happily compile programs that are wrong in ways the compiler cannot see, and some mistakes are undefined behavior — the language standard places no requirement on what happens at all. That does not mean "it picks a random sensible answer"; it means the optimizer is allowed to assume the bad thing never happens, so a broken program can appear to work today and corrupt data tomorrow. Our hello-world is safe, but keep the habit you will build later — check the return value of every call that can fail — and you will stay on the honest side of the line.