GCC is the compiler behind most C development on Linux, macOS, and embedded systems. Knowing how to drive it from the command line — which flags to use, which standard to target, how to enable debugging — separates developers who understand their tools from developers who just use an IDE and hope for the best.
The Minimal Compilation Command
gcc program.c -o programThis compiles program.c and writes the executable to program. Without -o, GCC writes to a.out on Unix systems.
The Flags You Should Always Use
gcc -Wall -Wextra -Werror -std=c11 -o program program.c-Wall— enable common warnings-Wextra— enable additional warnings-Werror— treat warnings as errors (forces you to fix them)-std=c11— compile as C11 (the current standard for most projects)
For a full breakdown of which warnings each flag enables, see our guide on GCC warning flags.
Language Standards
| Flag | Standard | When to use |
|---|---|---|
-std=c89 | C89 / ANSI C | Legacy code or maximum portability |
-std=c99 | C99 | Most embedded projects |
-std=c11 | C11 | Modern applications (recommended default) |
-std=c17 | C17 | Latest stable standard |
-std=gnu11 | C11 + GCC extensions | Linux kernel and system software |
Always specify a standard explicitly. Without it, GCC defaults to -std=gnu11 (GCC 5+), which includes extensions not in the standard — your code may not compile on other compilers.
Optimisation Levels
gcc -O0 -o program program.c /* no optimisation — fastest compilation, slowest runtime */
gcc -O1 -o program program.c /* basic optimisation */
gcc -O2 -o program program.c /* moderate optimisation — good default for production */
gcc -O3 -o program program.c /* aggressive optimisation — may increase binary size */
gcc -Os -o program program.c /* optimise for smallest binary size */Use -O0 during development (easier to debug). Use -O2 for release builds. Use -O3 only after profiling confirms the bottleneck.
Debug Symbols
gcc -g -o program program.c-g embeds debug information (variable names, function names, line numbers) into the binary. Required for GDB and for AddressSanitizer to show meaningful output. Always use during development:
gcc -g -O0 -Wall -Wextra -o program program.cDo not strip debug info with -s in development builds — you will lose the ability to trace crashes.
Compiling Multiple Files
/* Compile each file to an object file */
gcc -c -Wall -std=c11 main.c -o main.o
gcc -c -Wall -std=c11 utils.c -o utils.o
gcc -c -Wall -std=c11 math.c -o math.o
/* Link all object files into the final executable */
gcc main.o utils.o math.o -o programSplitting compilation into object files means only changed files need recompiling. A Makefile automates this.
Linking Libraries
gcc program.c -o program -lm /* link math library (sqrt, sin, cos) */
gcc program.c -o program -lpthread /* link POSIX threads */
gcc program.c -o program -lssl -lcrypto /* link OpenSSL */-l followed by a library name links that library. -lm links libm.so (the math library). Always put -l flags at the end of the command — the linker processes libraries left-to-right and resolves symbols in order.
Runtime Error Detection
/* AddressSanitizer — memory bugs */
gcc -g -fsanitize=address -o program program.c
/* UndefinedBehaviourSanitizer */
gcc -g -fsanitize=undefined -o program program.c
/* Both at once */
gcc -g -fsanitize=address,undefined -o program program.cThese instrument your binary to detect memory bugs, undefined behaviour, integer overflows, and more at runtime. Use them during development and testing. Do not ship instrumented binaries — they are 2–5× slower.
Defining Macros from the Command Line
gcc -DDEBUG program.c -o program /* defines DEBUG */
gcc -DVERSION=2 program.c -o program /* defines VERSION as 2 */
gcc -UDEBUG program.c -o program /* undefines DEBUG */This lets you toggle features without editing source files — useful for build scripts and CI pipelines.
Practical Full Command for Development
gcc -g -O0 -Wall -Wextra -Werror -std=c11
-fsanitize=address,undefined
-o program program.cYou can test your GCC compilation output immediately using our gcc compiler online — it uses GCC 4.8 under the hood and lets you verify your code before running it locally.
TL;DR
- Always use
-Wall -Wextra -Werror -std=c11as a baseline - Use
-g -O0during development,-O2for release builds - Link libraries with
-lat the end:-lm,-lpthread - Use
-fsanitize=address,undefinedduring testing to catch memory bugs - Always specify
-std=explicitly — don’t rely on GCC defaults - Compile to object files with
-c, link separately for multi-file projects