Getting Started — Environment & Build
The first step of CLI-based C learning. Install **gcc · Make** per OS, build a single file, and learn debug flags & Makefile in one go.
What you'll learn
- 1Install gcc and Make on Linux/macOS/Windows and verify versions.
- 2Build `hello.c` with `gcc` and run `./hello`.
- 3Understand the common compile flags (`-Wall`, `-g`, `-O2`, `-std=c11`).
- 4Write a minimal `Makefile` for a single-file project.
Development environment
This repository was verified on:
| Item | Version |
|---|---|
| OS | Linux (Ubuntu 24.04) |
| Compiler | gcc 13.3.0 |
| Standard | C11 (`-std=c11`) |
| Build tool | GNU Make |
Verify your environment
which gcc # /usr/bin/gcc
gcc --version # gcc 13.3.0
gcc -print-prog-name=cc1 # actual cc1 path
gcc -print-search-dirs # search paths
make --versionInstall per OS
| OS | Install command | Default path |
|---|---|---|
| Ubuntu / Debian | `sudo apt install build-essential` | `/usr/bin/gcc` |
| Fedora / RHEL | `sudo dnf install gcc make` | `/usr/bin/gcc` |
| macOS (Homebrew) | `brew install gcc` | `/opt/homebrew/bin/gcc-14` (Apple Silicon) |
| macOS (Xcode CLI) | `xcode-select --install` | `/usr/bin/gcc` (clang underneath) |
| Windows (MSYS2 UCRT64) | `pacman -S mingw-w64-ucrt-x86_64-gcc` | `C:\msys64\ucrt64\bin\gcc.exe` |
| Windows (MinGW-w64) | use installer | `C:\mingw64\bin\gcc.exe` |
Beginner recommendation — one per OS
| OS | Recommended | How to install |
|---|---|---|
| **Windows** | **MSYS2 (UCRT64)** — latest GCC + easy updates | run installer, then `pacman -S mingw-w64-ucrt-x86_64-gcc` |
| **macOS** | **Xcode Command Line Tools** — `gcc` is clang, good enough for learning | `xcode-select --install` |
| **Linux (Ubuntu/Debian)** | **build-essential** | `sudo apt install build-essential` |
Editor: **VS Code** + the C/C++ extension is recommended — debugger, autocomplete, build all wired up.
Core Concepts
1) Single-file build
gcc -std=c11 -Wall hello.c -o hello
./hello| Flag | Why |
|---|---|
| `-std=c11` | Pin to C11 standard |
| `-Wall` | Turn on common warnings (always use this) |
| `-Wextra` | Even more warnings |
| `-g` | Embed debug info (for `gdb`) |
| `-O0` / `-O2` | Optimization off / on |
| `-o name` | Set output filename |
2) Minimal Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -Wextra -g
hello: hello.c
$(CC) $(CFLAGS) $< -o $@
clean:
rm -f helloRun with `make`. `make clean` removes the output.
3) Debug build vs release build
| Purpose | Flags |
|---|---|
| Debug (development) | `-g -O0 -Wall -Wextra` |
| Release (deployment) | `-O2 -DNDEBUG` |
Mixing the two on the same binary makes line-stepping confusing — pick one.
How a C program is built — the four stages
`gcc hello.c -o hello` looks like one step, but it runs four stages in order. Knowing them makes error messages far less mysterious — most "weird" errors belong to a specific stage.
| Stage | What it does | Typical error |
|---|---|---|
| Preprocess (`-E`) | Expands `#include` / `#define`, strips comments | `No such file or directory` (missing header) |
| Compile (`-S`) | C → assembly; type checks & warnings happen here | `implicit declaration`, type warnings |
| Assemble (`-c`) | Assembly → object file (`.o`) | rare (bad inline asm) |
| Link | Joins object files + libraries into the executable | `undefined reference to ...` |
So `undefined reference` is a **link** error (a library wasn't linked, or a function was never defined), while `implicit declaration` is a **compile** error (a missing prototype). Same command, different stages.
gcc -E hello.c -o hello.i # 1. preprocess only
gcc -S hello.i -o hello.s # 2. compile to assembly
gcc -c hello.s -o hello.o # 3. assemble to an object file
gcc hello.o -o hello # 4. link into an executableAdd `-Wall -Wextra` and actually read the warnings — most beginner bugs (uninitialized variables, printf format mismatches) are caught here for free, before the program ever runs.
Examples
Example 1 — `hello.c`: build example from the main README — build with gcc then run ./hello
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}**Output**
Hello, World!Key: this single command does everything — `gcc hello.c -o hello && ./hello`.
Common Mistakes (FAQ)
Q. `gcc: command not found` — what now?
gcc isn't installed or isn't on your PATH. Install it first (`build-essential` on Ubuntu/Debian, Xcode Command Line Tools on macOS, MSYS2 UCRT64 on Windows), then re-open the terminal so the PATH refreshes.
Q. I ran `gcc hello.c` but there's no `hello` file — why?
Without `-o name`, gcc names the output `a.out` (or `a.exe` on Windows). Use `gcc hello.c -o hello`, and run it with `./hello` — on Linux/macOS the current directory isn't on PATH, so the leading `./` is required.
Q. `undefined reference to 'sqrt'` even though I `#include <math.h>`.
Including a header only *declares* the function; you must also *link* its library: `gcc prog.c -o prog -lm`. This is a link-stage error, not a compile error — the four-stage table above shows why.
Q. Do I really need `-std=c11`? It compiles without it.
gcc defaults to a standard (often gnu17) that quietly allows extensions. Pinning `-std=c11` keeps your code portable and predictable, and combined with `-Wall -Wextra` it warns when you drift into non-standard usage.
Q. Why do my breakpoints / line numbers look wrong in the debugger?
You likely built with optimization (`-O2`) or without `-g`. For debugging use `-g -O0`; optimization reorders and inlines code so source lines no longer map 1:1. Keep debug and release builds separate.
Recap
- One command: `gcc -std=c11 -Wall file.c -o name && ./name`.
- `-Wall` is non-negotiable in development.
- Use a tiny Makefile early; you'll add to it later.
Try it
echo '#include <stdio.h>\nint main(void){printf("Hello\n");return 0;}' > hello.c
gcc -std=c11 -Wall hello.c -o hello && ./hello💻 Examples
Compilable, runnable examples — see the output yourself.
#include <stdio.h>
int main(void) {
printf("Hello, World!\n");
return 0;
}
Hello, World!All lecture materials and example code are openly available on GitHub.
View on GitHub ↗