16 Hello world
What to know first
Looking back
Chapter 9 said a computer’s input and output is “a band of characters flowing a line at a time” (a stream), and that every program is given the flow called standard output by default. So what exactly is a program doing when it puts letters on the screen?
A. It is letting letters flow onto the band called standard output. The screen is merely today’s “paper” attached to the end of that band (remember that it is a descendant of the glass Teletype). What the first program does is exactly this — let one line of greeting flow onto standard output.
The need for this chapter, and its context
By the end of this chapter
The questions this chapter answers
- Why must built-ins and executables be told apart? They look the same to the user.
- Why is there no
/std:c23for MSVC? - I do not understand half of it. Why is
intthere, what is(void), and why of all things does 0 mean “without trouble”? - Why “Hello, world” of all greetings? Who decided it?
16.1 The first program#
By tradition, the first program greets the world.
examples-en/hello/hello.c
#include <stdio.h>
int main(void)
{
printf("Hello, world!\n");
return 0;
}
This is a kind of work plan written in the grammar of the C language, and it is called source code. It becomes an executable file we can run only after a translator called a compiler turns it into machine code and joins it with already-made, pre-built machine code (called a library). Look at that process, and at what the first program actually produces.
But one more thing is needed before that. Where and how is the executable run once it is made? That place is the terminal.
16.2 The terminal and the shell — where a program is run#
Chapter 9 showed “a band of characters flowing one line at a time”. Today’s window onto that band is the terminal. Open it and you can type characters; something answers what you typed.
Here we need to tell two names apart: terminal and shell. They are used loosely as one, but they do different jobs.
| Word | What it is | What it does |
|---|---|---|
| terminal | the window that displays characters and accepts input | passes keystrokes on and paints the characters that come back |
| shell | the program running inside that window | reads the line you typed, interprets it, runs it and returns the result |
Table 16.1 — Terminal and shell — the words sorted out
That is, the terminal is the glass, the shell is the worker behind it. “Typing a command into the terminal” is really “typing a command to the shell through the terminal”.
16.2.1 Which terminals (windows) and which shells (workers)#
| Terminal (window) | Shell (worker) | |
|---|---|---|
| Windows | Windows Terminal, the old console window | cmd.exe (Command Prompt), PowerShell (powershell / pwsh) |
| Linux, Unix | GNOME Terminal, Konsole, xterm and others | bash, zsh, dash, fish and others |
| macOS | Terminal.app, iTerm2 | zsh (the default), bash |
Table 16.2 — Terminals (windows) and shells (workers) on each platform
Which terminal window you use is a matter of taste; the result is the same either way. But a different worker means a different grammar — the same job is written differently, which is why, when copying a command off the internet, the first question is “whose shell is this written for?”.
How to tell which worker you are talking to also differs.
| Shell | How to tell |
|---|---|
bash, zsh | echo $SHELL or echo $0 |
cmd.exe | the prompt looks like C:\Users\me> |
| PowerShell | the prompt looks like PS C:\Users\me>; $PSVersionTable also shows the PowerShell version |
Table 16.3 — How to tell which shell you are in
16.2.2 What a shell does — read, split, run#
Given a line, a shell always takes the same three steps.
- Read — take the line up to the Enter.
- Split — cut it into words at the spaces. The first word is what to do; the rest are the arguments.
- Run — if the first word is something the shell knows itself, do it; otherwise find an executable file of that name and launch it.
The “something it knows itself” in that third step is a built-in command. It is fixed inside the shell, with no file of its own.
| To do this | bash, zsh | cmd.exe | PowerShell |
|---|---|---|---|
| which directory am I in | pwd | cd (with no argument) | Get-Location (pwd) |
| change directory | cd name | cd name | Set-Location (cd) |
| list files | ls | dir | Get-ChildItem (ls, dir) |
| print to the screen | echo hi | echo hi | Write-Output hi (echo) |
| clear the screen | clear | cls | Clear-Host (cls) |
Table 16.4 — The shell commands used on day one
The names in parentheses are aliases — PowerShell attached familiar names like ls and cd to the same jobs for people arriving from Unix. Just remember that the same name is a different program.
Q. Why must built-ins and executables be told apart? They look the same to the user.
A. Mostly they need not be. Three places demand it.
1. They are found differently. An executable is found by the shell searching the path (PATH); a built-in needs no searching, being inside the shell. So cd still works when PATH is broken.
2. Some jobs can only be built-ins. cd is the example. Changing directory changes the shell’s own state, and a separate program could only change its own state and exit (exactly chapter 10′s story about processes).
3. Names can collide. Where a built-in and an executable share a name the built-in usually wins. In bash, type name says whether a name is a built-in or a file.
16.2.3 Running the executable you made#
Compile the first program and one executable file appears. How it is run differs a little by platform, and this is where beginners get stuck most often.
| Situation | What to type |
|---|---|
Linux, macOS — hello in the current directory | ./hello |
Windows cmd.exe — hello.exe in the current directory | hello or .\hello.exe |
| Windows PowerShell | .\hello.exe (hello alone will not do) |
Table 16.5 — Running the executable you made — what to type in each situation
A common misconception. “I typed hello on Linux and got command not found”
Not because the file is missing. Because the shell does not search the current directory.
A shell looks for executables only in the directories listed in PATH, and the Unix family has a long-standing habit, for safety, of not putting the current directory in PATH — with it there, a nasty program planted in a directory under the name ls would run.
So you say “run this one, here” explicitly — the ./ of ./hello means “the current directory”.
PowerShell demands .\hello.exe for the same reason, while cmd.exe looks in the current directory first, so hello alone suffices. The three shells part company right here.
16.2.4 Passing arguments — how a program is handed values#
The shell cuts a line into words, we said. What follows the first word is passed to the program, and those are its arguments.
gcc -Wall -o hello hello.cHere gcc is what runs, and the four that follow are arguments. Arguments come in conventional kinds.
| Kind | Shape | Example |
|---|---|---|
| a value | a bare word | hello.c — the file to work on |
| a short option | one - + one letter | -c, -g; sometimes run together — -Wall |
| an option with a value | the next word is the value | -o hello — “the output is named hello” |
| a long option | two -- + a word | --version, --help |
| end of options | -- alone | everything after is a value — for file names starting with - |
Table 16.6 — Kinds of command-line argument
An argument containing a space is wrapped in quotes, because the shell splits words at spaces.
gcc -o "my program" hello.c # when the name has a spaceThese are the shell’s rules, not C’s. How a C program receives those arguments — main’s argc and argv — is covered in chapter 59.
Platform note. Windows and Unix split arguments differently
The Unix family has the shell cut the words and pass an array. Windows passes one string and the receiving runtime splits it (chapter 59). So quotes and backslashes are handled subtly differently, and a command line that worked on Linux can be cut differently on Windows.
One thing to remember at this stage: if a path contains a space, wrap it in quotes. Windows’ C:\Program Files\... is the classic place.
16.3 Compiling and running from the command line#
Now to turn that work plan — the source code hello.c — into an executable. There are many compilers, but their command lines fall into three patterns — GCC, Clang and MSVC. The first two share almost all their option syntax; only MSVC belongs to another lineage.
16.3.1 GCC (Linux, macOS, MinGW)#
gcc -std=c23 -Wall -Wextra -o hello hello.c
./helloWord by word: gcc is the program to call, -std=c23 says “read this as C23”, -Wall -Wextra says “tell me everything suspicious” (chapter 18 covers why these two are always on), -o hello says “name the result hello”, and the final hello.c is the material.
Drop the -o and the result comes out named a.out — a default handed down from early Unix, short for “assembler output”.
16.3.2 Clang (the macOS default; Linux and Windows too)#
clang -std=c23 -Wall -Wextra -o hello hello.c
./helloClang takes the same options as GCC. It was made that way on purpose, to drop in where GCC was, so switching between the two rarely means editing a command. It is also why this book can cross-check its listings with both.
On macOS, the gcc command often runs Clang — gcc --version shows which compiler is actually running.
16.3.3 MSVC (Windows)#
MSVC differs from the start, in its option syntax. And it must be run in its own window.
cl /std:c17 /W4 hello.c
hello.exe| To do this | GCC, Clang | MSVC |
|---|---|---|
| mark an option | one - | / or - (/W4) |
| choose the standard | -std=c23 | /std:c17, /std:c11, /std:clatest |
| turn warnings on | -Wall -Wextra | /W4 (/Wall is far too noisy) |
| name the output | -o hello | /Fe:hello.exe |
| compile only | -c | /c |
Table 16.7 — The first build command, per compiler
A window of its own is needed because MSVC relies on several environment variables (the paths to headers and libraries). The Start menu’s “x64 Native Tools Command Prompt for VS” opens a cmd with those variables set. Type cl in an ordinary cmd and you get “not recognized as an internal or external command” — not because the file is missing but because PATH was not set up, as the misconception box in “Running the executable you made” explained: a shell looks for executables only in the directories listed in PATH.
Q. Why is there no /std:c23 for MSVC?
A. Because, as this is written, MSVC’s C support reaches c11, c17 and clatest. Microsoft long treated C as an appendage of C++ and was late even to C99 (chapter 19); it is catching up now but still trails GCC and Clang.
So to run this book’s listings on Windows, one of three is realistic — MinGW-w64′s GCC, Clang for Windows, or GCC inside WSL (Windows Subsystem for Linux). To insist on MSVC, pass /std:clatest and be ready for examples that use C23 features (nullptr, constexpr and the like) to run into trouble.
Platform note. Three compilers, side by side
| GCC | Clang | MSVC | |
|---|---|---|---|
| where | the Linux default; Windows via MinGW | the macOS default; Linux and Windows | Windows only |
| option lineage | Unix-style - | same as GCC | the / lineage |
| C23 support | broad | broad | catching up |
| running it | ./hello | ./hello | hello.exe |
Table 16.8 — The three compilers compared — where, option lineage, C23 support, running
All three target the same C standard, so this book’s portable listings should give the same answer whichever C23-capable compiler builds them — and where they do not, that place is exactly what this book calls implementation-defined, or a grey area (chapter 13). In practice this book builds with GCC and cross-checks with Clang. MSVC is, as the table says, still catching up on C23, so listings that use C23 features may not build there yet.
16.4 And so the first program comes out like this#
Here is the result of the two lines just typed — compile, then run.
Output
Hello, world!
As with every result in this book, it is not decoration but what the machine really produced — every build compiles and runs the listing and puts its output here.
16.5 Reading it line by line#
Having seen it run, we now read what each of those six lines says.
#include <stdio.h> — a declaration of what we need. It means “fetch the standard input/output toolbox”, and the printf used below lives in that box. The name stdio takes the bold letters from standard input/output — under the file-name length limits of early C such compressed names became the practice, which is why most standard header names are short.
The precise identity of lines beginning with # is revealed in the next chapter (preprocessing).
int main(void) — the agreed starting point. A C program that runs on top of an operating system, as the one we are making does (a hosted implementation), starts at the function named main. To run the program is to run main. Where there is no operating system, even the name of the starting point can differ — that story is in chapter 59. The meanings of the parentheses, of int and of void are unravelled in turn in Parts IV and V.
{ and } — the fences of beginning and end. Inside the fence is the list of things main will do.
printf("Hello, world!\n"); — the substance of this program. It calls the tool named printf and lets the letters inside the quotation marks flow onto the band of standard output. \n is the very newline character we met in chapter 9 — the typewriter’s legacy is already in the first program. The semicolon at the end is the full stop of a statement.
return 0; — the parting bow. It ends main and reports 0 to the operating system, meaning “finished without trouble.”
Q. I do not understand half of it. Why is int there, what is (void), and why of all things does 0 mean “without trouble”?
A. It is correct not to know yet — and this book manages that “no need to know” honestly. There are only two things to take away from these six lines right now: that the host program we are making starts at main, and that printf lets letters flow onto standard output. All the rest is explanation deliberately put off — Part IV takes as its goal the dissection of this one piece from beginning to end, and the explanations come for statements and blocks in chapter 20, for printf and calls in chapters 22–23, and for int and (void) in chapters 24–26. Not explaining everything now is not laziness but order.
Q. Why “Hello, world” of all greetings? Who decided it?
A. Tradition — and it has a lineage. This greeting was born beside C’s fathers. Brian Kernighan used “hello, world” as the first example in a language tutorial at Bell Labs in the early 1970s, and when the 1978 K&R book (that “de facto standard” we met in chapter 13) carried it as the first program it became a worldwide tradition. Since then the first example of almost every language imitates this greeting. It has become idiom enough that on meeting a new language or a new environment people say “start by getting hello world up” — and we have just joined that tradition.
16.6 But what happened between source and execution?#
We have just seen two objects: the source code a human reads and writes (hello.c), and the result the machine produced. But as chapter 2 taught, what a machine can execute is machine instructions only, and source code is just a text file — in the terms of chapter 8, a sequence of letters held in UTF-8. So somewhere in between there was a large event in which text became machine instructions.
The name of that event is compilation, and the next chapter tours the scene stage by stage. Seen from outside it is a single command; inside, a relay of four stages turns — in one of which our “spell” #include is broken, and in the last of which it is revealed where the substance of printf comes from.