C lesson 1 of 8
Your First C Program
What every C program must contain - the main function, printf and its format specifiers, escape sequences, and what a compiler actually does to your source file before anything runs.
Published · Every example on this page was run before it was published.
C does not run your file. It translates it. Before a single character appears on screen, a separate
program called a compiler reads your source text and turns it into machine instructions, and only
that translated result is ever executed. That one fact explains most of what feels strange about C at
the start: why every program needs a main, why you have to announce which library functions you
intend to use, and why a misplaced semicolon stops you before anything runs at all. This lesson gets a
program onto the screen, then explains each part of it.
The Smallest Complete Program
Here is a complete C program. Nothing has been left out.
#include <stdio.h>
int main(void) {
printf("Hello from C.\n");
return 0;
}Hello from C.Four things are doing work there.
#include <stdio.h> is a line for the preprocessor, a step that runs before the compiler proper
and does simple text editing on your file. This particular line pastes in the contents of the standard
header stdio.h, which describes the standard input/output functions - printf among them. Without
that line the compiler would reach printf and have no idea what it is or what arguments it takes. The
# marks a preprocessor directive, and directives never end in a semicolon.
int main(void) declares a function named main. Every C program needs exactly one, because main is
where execution begins - the compiled program starts at the first line of main and stops when main
finishes. The int in front says main hands back an integer when it is done. The void inside the
parentheses says this main takes no arguments; writing empty parentheses instead is legal but means
something looser, so (void) is the clearer habit.
The braces { and } mark the beginning and end of the function's body: the statements that run,
in order, from top to bottom.
return 0; ends main and hands the value 0 back to whatever started the program. By long convention
0 means "finished normally" and any other number means something went wrong. Every statement inside a
function body ends with a semicolon - the semicolon is what separates one statement from the next, not
the newline, so C does not care how you spread a statement across lines.
printf Prints Exactly What You Tell It To
printf writes text to the program's output. It does not add a line break of its own - if you want one,
you put one in the text yourself, as \n.
#include <stdio.h>
int main(void) {
printf("first");
printf("second\n");
printf("third\n");
return 0;
}firstsecond
thirdThree calls, two lines of output. The first two calls both wrote onto the same line because only the
second one ended with \n. This trips up everyone once, and it is worth meeting on purpose now rather
than wondering later why two pieces of a report ran together.
\n is an escape sequence: a backslash followed by another character, which together stand for one
character that would be awkward or impossible to type inside quotes. The ones you will reach for
constantly are \n for a newline, \t for a tab, \\ for a single literal backslash, and \" for a
double quote inside a double-quoted string.
#include <stdio.h>
int main(void) {
printf("A backslash: \\\n");
printf("A double quote: \"\n");
printf("Two lines from one call:\nhere is the second\n");
return 0;
}A backslash: \
A double quote: "
Two lines from one call:
here is the secondThe first line needed three backslashes in a row in the source: \\ produces one backslash, and the
\n that follows produces the newline. Read escape sequences in pairs from left to right and they stop
being confusing.
Format Specifiers: Printing Values, Not Just Text
The real job of printf is assembling text out of values. Inside the quoted string you leave a
placeholder beginning with %, and you pass the value to fill it as a further argument after the
string. The placeholder is called a format specifier, and which one you use depends on the type of
the value.
#include <stdio.h>
int main(void) {
int crates = 7;
double mass = 12.5;
char grade = 'B';
printf("Crates: %d\n", crates);
printf("Mass: %f\n", mass);
printf("Mass: %.2f\n", mass);
printf("Grade: %c\n", grade);
printf("Label: %s\n", "dry goods");
return 0;
}Crates: 7
Mass: 12.500000
Mass: 12.50
Grade: B
Label: dry goods%d prints an int in decimal. %f prints a double, and on its own it always shows six digits after
the point, which is why 12.5 came out as 12.500000. Writing %.2f instead asks for exactly two
digits after the point. %c prints a single character - note that a single character is written between
single quotes, 'B', while text of any length goes between double quotes. %s prints a
string.
The next lesson covers what int, double and char actually mean. For now the point is that the
specifier and the value have to agree: %d expects an int, %f expects a double, and handing one
the other is a mistake the compiler will normally warn you about but is not obliged to reject.
One printf call can carry as many specifiers as you like. They are filled left to right from the
arguments that follow, and %% prints a literal percent sign, since a lone % would be read as the
start of another specifier.
#include <stdio.h>
int main(void) {
int done = 18;
int total = 24;
printf("%d of %d tasks done (%.1f%%)\n", done, total, 100.0 * done / total);
return 0;
}18 of 24 tasks done (75.0%)Three specifiers, three arguments, in that order. The third argument is an expression rather than a
plain variable, which is fine - printf receives whatever it evaluates to.
What the Compiler Actually Does
When you press Run on this site, your editor's contents are saved as a file named main.c and handed to
a real C compiler: clang, built to target WebAssembly through Emscripten, invoked roughly as
emcc -O0 -fexceptions main.c. The -O0 asks for no optimisation, so the machine code stays close to
what you wrote. The result is WebAssembly that runs in this browser tab. On your own machine the same
source would go through a compiler such as gcc or clang and come out as an executable file you could
run from a terminal, but the stages are the same either way:
- Preprocessing. Every
#includeis replaced by the contents of that header, and every macro is expanded. What the compiler proper sees is a much longer file than the one you typed. - Compiling. The expanded source is checked against the rules of the language and translated into machine instructions. This is the stage that reports errors.
- Linking. Your compiled code is joined to the compiled standard library, so the call you wrote to
printfis connected to the realprintf. A program that compiles cleanly can still fail here, if it calls something that exists nowhere.
C has had several official versions, and this site compiles with the default the toolchain ships with, C17. Everything in these lessons is ordinary C that has been valid for a long time, so it will build the same way with any modern compiler.
When It Does Not Compile
If the compiler cannot make sense of your file, nothing runs - not even the lines before the mistake,
because translation never finished. Instead you get a diagnostic: a message that names the file, the
line and the column it gave up at, in the form main.c:5:30:, followed by a description of the problem
and a copy of the offending line with a caret under the exact spot.
Three mistakes produce almost all early diagnostics:
- A missing semicolon. The compiler carries on reading, decides the next line is part of the same statement, and complains about something that looks fine. When a message points at a line you are sure about, check the line above it.
- A missing
#include. Callingprintfwith no<stdio.h>means the compiler has never heard of the name. The message will talk about an undeclared function, not aboutprintfbeing wrong. - Unbalanced braces or parentheses. One missing
}usually produces a complaint at the very end of the file, because that is where the compiler finally runs out of text.
Read diagnostics from the top down and fix the first one before looking at the rest; a single early mistake often generates a cascade of later complaints that vanish on their own.
The Return Value of main
return 0; is a message to whoever launched the program. Change it to return 3; and the program still
prints everything it printed before, but this site adds a line of its own underneath the output:
The program exited with status 3. That is not your program talking - it is the runner reporting the
number main handed back. Nonzero means "something went wrong", which is why command-line tools use it
to signal failure, and why you should leave main returning 0 unless you mean otherwise.
A Worked Example
This program uses nothing beyond what is above: three variables, some arithmetic, and one printf per
line of the report.
#include <stdio.h>
int main(void) {
int shifts = 3;
int crates_per_shift = 18;
double hours_per_shift = 7.5;
int total_crates = shifts * crates_per_shift;
double total_hours = shifts * hours_per_shift;
printf("Station %s\n", "Bay 4");
printf("Shifts: %d\n", shifts);
printf("Crates handled: %d\n", total_crates);
printf("Hours worked: %.1f\n", total_hours);
printf("Crates per hour: %.2f\n", total_crates / total_hours);
return 0;
}Station Bay 4
Shifts: 3
Crates handled: 54
Hours worked: 22.5
Crates per hour: 2.40shifts * crates_per_shift multiplies two whole numbers and gives 54. shifts * hours_per_shift
mixes a whole number with a decimal one, and C widens the whole number to match, giving 22.5. The last
line divides 54 by 22.5, which is 2.4, and %.2f pads it out to two decimal places as 2.40 -
printf never drops a digit you asked for.
Notice that "Bay 4" was passed straight to %s without being stored anywhere first. A quoted string
is a value like any other; a variable is only needed when you want to use it more than once or give it a
name that explains it.
Reading Input
C can read from the keyboard too, with scanf for individual values or fgets for a whole line, and
this site gives you an input box next to the editor to type into. None of the examples in these lessons
use it, so that every program on the page produces the same output for everybody who runs it. When you
are ready to experiment, remember that a program which waits for input it never receives will simply
sit there until it times out.
Common Mistakes
Forgetting the \n. The most common cosmetic bug in early C. printf adds nothing you did not ask
for, so several calls in a row all land on one line. If your output looks like one long smear, count
your newlines.
Putting a semicolon after a preprocessor directive. #include <stdio.h>; is not a statement, and
the stray semicolon is left behind as one, which confuses the compiler at the top of your file. No #
line ever takes a semicolon.
Mixing up 'a' and "a". Single quotes make one character, a value of type char. Double quotes
make a string, which occupies one byte more than the characters you see because of the invisible
terminator on the end - a detail the arrays-and-strings lesson goes into properly. They are different
types, so %c and %s are not interchangeable.
Expecting a specifier to convert a value. %d does not turn a decimal number into a whole one, and
%f does not turn a whole number into a decimal one. A specifier describes what type the value already
is; mismatch it and you have told printf to read the bytes it was handed as a type they are not, which
is undefined behaviour - anything at all may be printed. Conversions are your job, and the next lesson
shows how to ask for one.
Leaving out return 0;. Since C99 this is actually allowed in main specifically, and reaching the
closing brace behaves as if you had returned 0. It is still worth writing, because the habit is the
same one every other function needs, and there the return really is required.
Assuming the compiler runs your code to find problems. It does not. It checks the shape of your program - are these names declared, do these types fit together, is this syntax legal - and nothing more. A program that divides by zero, or reads past the end of an array, compiles perfectly happily. Catching that class of problem is the subject of most of the rest of this track.
Next Steps
Open the C playground and type the five-line program at the top of this lesson
from memory, without copying it. Then break it on purpose, one thing at a time: delete the semicolon
after the printf, delete the #include, delete the closing brace. Read each diagnostic and note which
line it blames, because they are not always the line you changed.
Then print a small table of your own with three printf calls, using %d for a count, %.2f for a
price and %s for a label. The next lesson explains what those three types really are, how much memory
each one takes, and the one arithmetic surprise that catches nearly every C beginner.
Write it yourself
Reading about code and writing it are different skills. These exercises practise exactly what this lesson covered; they run in this tab and need no account.