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 program

This 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

FlagStandardWhen to use
-std=c89C89 / ANSI CLegacy code or maximum portability
-std=c99C99Most embedded projects
-std=c11C11Modern applications (recommended default)
-std=c17C17Latest stable standard
-std=gnu11C11 + GCC extensionsLinux 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.c

Do 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 program

Splitting 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.c

These 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.c

You 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=c11 as a baseline
  • Use -g -O0 during development, -O2 for release builds
  • Link libraries with -l at the end: -lm, -lpthread
  • Use -fsanitize=address,undefined during 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