Module 1 · Meet C: Your First Programs, Symbol by Symbol
Compile and Run It Yourself: the Playground and a Local Toolchain
In this lesson
- Run a program in the Playground, with input, and read its output pane.
- Compile and run the same program on your own machine, if you want one.
- Turn on
-Wall -Wextra, read the warning it gives, and say what-E,-S,-cand-lmdo.
The Playground is a real GCC in a sandbox on a machine in Singapore. A local toolchain is the same GCC on your own disk. Neither is the serious one; they are the same compiler in two places.
So this lesson is not an instruction to install anything. Everything in this track runs in your browser, forever. Read the second half when you want your own copy, and skip it happily until then.
The Playground: a real GCC, on somebody else's machine
Every example in this track has a Run link, which opens the Playground with the program already loaded. Four parts of that page matter.
The editor holds your program. The Run button sends it away, and a second later the output pane shows what it printed. The input box is where you type what the program reads, which is how every problem in this module is tested.
Your file is written to disk on that machine, compiled, and run inside a sandbox with a time limit and a memory limit. If your program waits for input that never comes, the time limit ends it, and that is a real result rather than a hang.
There is also a limit on how many runs a minute one visitor gets. A "too many requests" message means wait, not break.
So a Playground run is a compile and a run on a real machine. The one thing it hides from you is the command line.
The exact command the Playground uses
It is this, and it is pinned, which means you cannot change it from the page:
gcc -O2 -std=c17 -lm
Three things follow from it, and the third one is the one to remember.
-O2 asks for optimised machine code, so the program runs fast. -std=c17 is the standard this track teaches. -lm is already there, so the maths library is always linked for you.
And there is no -Wall on it. The Playground prints the messages GCC gives by default, and nothing more.
That has a consequence worth stating plainly. A message you see on your own machine may be silent in the Playground. An unused variable, a %s handed a number, an if with one =: all three are quiet there and all three are warnings under -Wall.
The reason is a rule rather than an oversight. The compiler options are fixed per language and the list of options a client may add is empty. Every reader gets the same toolchain.
So the Playground is the honest floor: if it builds there, it builds for your teacher too. Your own machine is where you go looking for more warnings than the floor requires.
Installing GCC, on three operating systems
Only if you want to. Five lines each.
Linux. Open a terminal. On Debian or Ubuntu run sudo apt update and then sudo apt install build-essential. On Fedora run sudo dnf install gcc. Check it with gcc --version. You are done.
macOS. Open Terminal and run xcode-select --install, then accept the dialog that appears. That installs the Command Line Tools, which include a compiler. Check it with gcc --version. On macOS that name actually runs Clang, which is fine for everything in this track.
Windows, the recommended way. Install WSL, the Windows Subsystem for Linux, by running wsl --install in PowerShell as administrator and restarting. Open the Ubuntu app it gives you and follow the Linux instructions above. You now have a real Linux toolchain beside Windows, which is what most professional C work uses.
Windows, without WSL. Install MSYS2 from its own site, open its UCRT64 shell, and run pacman -S mingw-w64-ucrt-x86_64-gcc. Add its bin folder to your PATH if you want gcc in other terminals. This is a real GCC, and one difference matters below.
So every path ends in the same place: a gcc you can type at a prompt.
The four commands you will actually type
Save a program as hello.c and you are four commands away from everything in this module.
- Compile.
gcc -std=c17 -Wall -Wextra hello.c -o helloreads your file and writes a program calledhello. - Run.
./helloon Linux and macOS;hello.exeor.\hello.exeon Windows. - Read the status.
echo $?prints the numbermainreturned, which lesson 1 promised was not decoration. - Link the maths library.
gcc -std=c17 hello.c -o hello -lm, needed on Linux whenever you callsqrt,poworsin.
Two notes about that last one. On Linux the maths code sits in a separate library file. Leaving out -lm gives you undefined reference to 'sqrt' from the linker, which is Module 0, lesson 4 in action. On Windows with MSYS2, and on macOS, it links without the flag. The Playground passes -lm always, so the question never arises there.
So -Wall -Wextra is the habit, and -lm is the flag you will forget exactly once.
The stage flags: stopping the pipeline early
Module 0, lesson 4 described four stages: preprocessor, compiler, assembler, linker. Three flags stop after each of the first three, and they are the cheapest way to see that the stages are real.
| Flag | Stops after | What you get | Is it text? |
|---|---|---|---|
-E | The preprocessor | hello.i: your file with every header pasted in | Yes, and it is long |
-S | The compiler | hello.s: assembly for your CPU | Yes, and your strings are in it |
-c | The assembler | hello.o: machine code, not yet a program | No, do not open it |
| none | The linker | hello: a program you can run | No |
Try gcc -E hello.c | wc -l once. Seven lines of yours become more than a thousand, which is stdio.h arriving. It explains why a missing header is a stage 1 failure.
Reading your first warning
A warning is the compiler saying "this is legal, and I do not believe you meant it". It never stops the build.
The three you will meet first, all of them under -Wall and all of them silent without it:
warning: unused variable 'total'. You declared it and never used it, which usually means an edit is half finished.warning: format '%s' expects argument of type 'char *', but argument 2 has type 'int'. The placeholder and the value disagree, and this one usually ends in a crash.warning: suggest parentheses around assignment used as truth value. The=and==trap from lesson 2, caught.
Treat every one as an error you have not been bitten by yet. Beginners who build with warnings on find bugs days earlier than beginners who do not, and professional projects turn warnings into errors on purpose.
So a clean build under -Wall -Wextra is a stronger claim than a clean build in the Playground, and that is the only way the two differ.
The gcc command line, part by part
gcc -std=c17 -Wall -Wextra hello.c -o hello
| | | | |
| | | | the name of the program to write
| | | your input file
| | turn on the useful warnings
| pick the language standard
the program that runs all four stages for you
- Leave out
-o helloand you get a file calleda.out, ora.exeon Windows. -std=c17matters: the same file can be legal C17 and refused as C89.-Wall -Wextraare two flags, not one.-Wallis not actually all of them.- Add
-lmat the end, after your file, when you call a maths function on Linux.
Nothing new in the C. The point is the command: this file builds clean under -Wall -Wextra, so anything the compiler says to you later is about your edit.
#include <stdio.h>
int main(void)
{
int physics = 37;
int chemistry = 41;
int total = physics + chemistry;
printf("Total: %d\n", total);
printf("Subjects: 2\n");
return 0;
}
Total: 78
Subjects: 2
Save it as marks.c, build it with gcc -std=c17 -Wall -Wextra marks.c -o marks, and run ./marks. A clean build prints nothing, which surprises everybody once.
One line is added. The output does not change at all, and the compiler now has something to say, but only if you asked it to.
#include <stdio.h>
int main(void)
{
int marks = 90;
int total = 0; /* declared, then never used again */
printf("Marks: %d\n", marks);
return 0;
}
Marks: 90
In the Playground: no message. With gcc -std=c17 -Wall -Wextra: warning: unused variable 'total' [-Wunused-variable] on the line that declares it.
It is a small warning with a large habit behind it. Nine times out of ten an unused variable is a line you meant to finish, and the tenth is a variable you should delete.
Run in CompilerA maths function, a real answer, and the one flag beginners meet before they understand linking.
#include <math.h>
#include <stdio.h>
int main(void)
{
double side = 5.0;
/* sqrt is declared in math.h and lives in the maths library. */
double diagonal = side * sqrt(2.0);
printf("Diagonal: %.3f\n", diagonal);
return 0;
}
Diagonal: 7.071
In the Playground this runs as written, because -lm is on the pinned command line. On Linux, gcc diagonal.c -o diagonal gives you undefined reference to 'sqrt' and gcc diagonal.c -o diagonal -lm does not. On macOS and MSYS2 it links either way.
Same source, same compiler, three different answers about one flag. That is the linker being a separate program, exactly as Module 0 said.
Run in CompilerWhere this is used
- Every CI pipeline in the world. A build server runs these same commands with no human watching, and a warning-free build is often a merge requirement. The habit you form here is the habit the job needs.
- The Linux kernel. Its build turns on a long list of warning flags and treats many as errors. At 30 million lines, a warning nobody reads is a warning nobody fixes.
- The Progsity runner. The machine behind the Run button compiles your file with the pinned command line and runs it in a sandbox. Then it sends back what your program printed. Same GCC, someone else's disk.
- Every Makefile you will ever read. A Makefile is mostly these flags, written down once so that nobody has to remember them, which is Module 15.
Common mistakes
1. Looking for a program called hello when you never named it.
gcc hello.c
./hello
The shell answers bash: ./hello: No such file or directory. There is no message from GCC, because it did what you asked: it wrote a.out. Add -o hello, or run ./a.out.
2. Forgetting -lm on Linux.
gcc -std=c17 diagonal.c -o diagonal
The linker says undefined reference to 'sqrt', and the compiler said nothing at all, because math.h promised the function exists. This is the clearest example in the whole track of stage 1 and stage 4 disagreeing.
3. Editing the file and running the old program.
./hello
No message, and the output is your previous version, which can cost you an hour of confusion. A source file is not a program. Compile again after every edit, or let a Makefile remember for you.
4. Assuming the Playground and your machine agree about warnings.
int n = 5;
printf("%s\n", n);
In the Playground: nothing, because the format check lives behind -Wall. Locally with -Wall: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'int'. Both are the same GCC 12 behaviour under different flags, and the program is broken in both places.
Remove the warning from this program without changing a single character of its output.
#include <stdio.h>
int main(void)
{
int marks = 90;
int bonus = 5;
printf("Marks: %d\n", marks);
return 0;
}
What the compiler says. With -Wall -Wextra: warning: unused variable 'bonus'. In the Playground: nothing, so this exercise is one you do on your own machine or by reading carefully.
Two fixes, one right answer. Deleting the line removes the warning. Using the variable in the printf removes the warning and changes the output, which the task forbids. Decide which one the task allows, and say in one sentence why the other is worse.
Check yourself. Build with gcc -std=c17 -Wall -Wextra and see nothing at all. The output must still be exactly Marks: 90.
Write a program that prints 2 raised to the power 10 using pow from math.h, and add one comment that explains why a Linux build needs -lm.
Output. Exactly one line: 2 to the 10 is 1024.
Rules. Use pow(2.0, 10.0) and print it with %.0f, because pow returns a double. The comment must mention the linker, and it must not simply repeat the flag.
#include <math.h>
#include <stdio.h>
int main(void)
{
/* Your one comment goes here, about the linker. */
printf("2 to the 10 is %.0f\n", pow(2.0, 10.0));
return 0;
}
Check yourself. It runs unchanged in the Playground. On Linux, build it once without -lm to see the linker error, then once with it. Reading that error on purpose is the point of the exercise.
Common doubts
Do I have to install a compiler to finish this track?
No. Every lesson, every example and every graded problem runs in the Playground. Install one when you want to build a project of several files, which is Module 15.
WSL or MSYS2 on Windows?
WSL, if you can. It gives you the same Linux toolchain that servers and CI use, so error messages and flags match what you read online. MSYS2 is lighter and perfectly good for this track.
Why can I not add
-Wallin the Playground myself?Because the command line is fixed per language and the list of options a page may add is empty, on purpose. Everybody's program is compiled the same way, which is what makes a graded run fair.
Is Clang acceptable instead of GCC?
Yes. It accepts everything in this track and its messages are often clearer. Keep in mind that the exact wording of a warning differs, so a message here may not match yours word for word.
Can I use an IDE instead of a terminal?
Of course, and most professionals do both. Learn the four commands anyway: an IDE's build button is running them, and when it fails, the terminal is where you find out why.
Key takeaways
- The Playground is a real GCC 12 in a sandbox, with the command line
gcc -O2 -std=c17 -lmpinned. - There is no
-Wallthere, so a warning you see locally can be silent in the Playground. - On your own machine,
gcc -std=c17 -Wall -Wextra file.c -o nameis the habit to build. - A clean build prints nothing at all.
-E,-Sand-cstop after the preprocessor, the compiler and the assembler.-lmlinks the maths library, which Linux needs and the Playground already passes.
That closes Part Zero. Next comes the module's graded problem set, ten problems with hidden tests, and then the module test.
End of lesson 6
Mark it done, and your progress moves with you.
Next: Problems: First Programs