15 Hello world
What to know first
Looking back
Chapter 10 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?
15.1 The first program
By tradition, the first program greets the world.
examples-en/ch15/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). Let us 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.
15.2 The terminal and the shell — where a program is run
Chapter 10 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.
Two words must be told apart here. They are used loosely as one, and they do different jobs.
| Word | What it is | What it does |
|---|---|---|
| terminal | the window that shows and takes characters | 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 15.1
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”.
15.2.1 Which windows and which 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 15.2
The window may be chosen by taste and the result is the same. 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 tells |
Table 15.3
15.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 15.4
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 3′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.
15.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 15.5
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.
15.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 15.6
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 treated properly in chapter 53.
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 3). 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.
15.3 Compiling and running from the command line
Now to turn that work plan into an executable. There are many compilers, but the shapes reduce to three — GCC, Clang and MSVC. The first two share almost all their option syntax; only MSVC belongs to another lineage.
15.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 17 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”.
15.3.2 Clang (the macOS default; Linux and Windows too)
clang -std=c23 -Wall -Wextra -o hello hello.c
./helloThe options are GCC’s. It was built that way deliberately (chapter 18), so moving between the two rarely means editing a command. It is also why this book can cross-check its listings with both.
On macOS, typing gcc often calls Clang — gcc --version reveals the truth.
15.3.3 MSVC (Windows)
MSVC differs from the option syntax up. 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 15.7
The own window is needed because MSVC leans 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 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 prepared, exactly the previous section’s story.
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 18); 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. To insist on MSVC, pass /std:clatest and be ready for the places where C23 features (nullptr, constexpr and the like) catch.
Platform note. The three compilers in one line each
| 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 15.8
All three aim at the same standard, so this book’s listings should give the same answer whichever builds them — and where they do not, that place is exactly what this book calls implementation-defined, or a grey area (chapter 12). In practice this book builds with GCC and cross-checks with Clang.
15.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.
15.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 53. 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 10 — 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 deliberate credit — Part IV takes as its goal the dissection of this one piece from beginning to end, and the debt is repaid for statements and blocks in chapter 19, for printf and calls in chapters 21–22, and for int and (void) in chapters 23–25. 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 meet in chapter 12) 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.
15.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 4 taught, what a machine can execute is machine instructions only, and source code is just a text file — in the terms of chapter 9, 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 “order” #include is filled, and in the last of which it is revealed where the substance of printf comes from.