Skip to content

GCC command-line cheatsheet from first compilation to debugging, linking, optimization, and production builds.

GCC is the GNU Compiler Collection. For ordinary projects:

CommandUse It For
gccCompile and link C programs
g++Compile and link C++ programs; automatically links the C++ standard library
cppRun only the C/C++ preprocessor
gcovRead coverage data generated by GCC

The basic pattern is:

Terminal window
gcc [options] input.c -o output
g++ [options] input.cpp -o output

Examples on this page use Unix-style executable names (./app). On Windows with MinGW, run the output as app.exe.


hello.c
#include <stdio.h>
int main(void) {
puts("Hello, GCC!");
return 0;
}
Terminal window
gcc hello.c -o hello # Compile and link
./hello # Run on Linux/macOS
hello.cpp
#include <iostream>
int main() {
std::cout << "Hello, GCC!\n";
}
Terminal window
g++ hello.cpp -o hello # Use g++ for C++ linking
./hello
CommandMeaning
gcc main.cBuild C program as a.out (a.exe on some Windows setups)
gcc main.c -o appName the output executable app
gcc -std=c17 main.c -o appCompile using the C17 language standard
g++ -std=c++20 main.cpp -o appCompile C++20 source
gcc --versionPrint GCC version
gcc -v main.c -o appShow commands and paths used while building
Terminal window
gcc -std=c17 -Wall -Wextra -Wpedantic -g main.c -o app
FlagPurpose
-std=c17Select a defined C standard
-WallEnable many common warnings
-WextraEnable additional useful warnings
-WpedanticWarn about non-standard language extensions
-gInclude debug information for a debugger

Warnings are not failures by default. Fix warnings instead of ignoring them.


GCC usually performs four stages:

source.c -> preprocessed source -> assembly -> object file -> executable
preprocessing compile assemble link
StageGCC OptionTypical OutputWhat Happens
Preprocess-E.iExpands #include, #define, and conditional compilation
Compile-S.sTranslates C/C++ into assembly
Assemble-c.oProduces machine-code object file without linking
Linkdefault final stepexecutable/libraryResolves functions and combines object files/libraries
Terminal window
gcc -E hello.c -o hello.i # Stop after preprocessing
gcc -S hello.c -o hello.s # Stop after generating assembly
gcc -c hello.c -o hello.o # Stop after object file
gcc hello.o -o hello # Link object into executable
Terminal window
gcc -save-temps hello.c -o hello

This is useful when learning, investigating macros, or inspecting generated assembly.


OptionExamplePurpose
-o file-o calculatorSet output filename
-cgcc -c util.cCompile only; do not link
-Egcc -E config.cPreprocess only
-Sgcc -S math.cGenerate assembly
-x languagegcc -x c input.txtTreat input as a particular language
-pipegcc -pipe main.cUse pipes between compilation stages where supported
LanguageCommon Selection
C90-std=c90
C11-std=c11
C17-std=c17
GNU C17 extensions-std=gnu17
C++17-std=c++17
C++20-std=c++20
GNU C++20 extensions-std=gnu++20

-std=c17 is stricter and more portable; -std=gnu17 also allows GNU extensions commonly used on Linux.

OptionExamplePurpose
-DNAME-DDEBUGDefine macro with value 1
-DNAME=value-DPORT=8080Define macro with a value
-UNAME-UDEBUGUndefine a macro
-I dir-IincludeAdd header search directory
-iquote dir-iquote src/includeSearch directory for quoted includes only
-isystem dir-isystem vendor/includeAdd system header directory with reduced warning noise
#ifdef DEBUG
fprintf(stderr, "x = %d\n", x);
#endif
Terminal window
gcc -DDEBUG -Iinclude src/main.c -o app

Terminal window
# Everyday C development
gcc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -Wshadow main.c -o app
# Everyday C++ development
g++ -std=c++20 -Wall -Wextra -Wpedantic -Wconversion -Wshadow main.cpp -o app
# Continuous integration: reject newly introduced warnings
gcc -std=c17 -Wall -Wextra -Wpedantic -Werror main.c -o app
OptionCatches or Controls
-WallFrequently useful warnings such as unused values and suspicious constructs
-WextraAdditional parameter, comparison, and initializer warnings
-WpedanticExtensions beyond the selected standard
-WconversionImplicit conversions that may alter a value
-Wsign-conversionSigned/unsigned conversions
-WshadowDeclarations hiding earlier names
-Wformat=2Stricter printf/scanf format checking
-WundefUnknown identifiers used in preprocessor #if expressions
-WerrorTreat warnings as errors
-Wno-error=nameKeep one warning non-fatal under -Werror

-Wall does not mean every possible warning. Strong warning flags can be noisy on legacy or third-party code, so introduce them deliberately.

Terminal window
gcc -fdiagnostics-color=always -fdiagnostics-show-option main.c -o app
gcc -fmax-errors=5 main.c -o app
OptionPurpose
-fdiagnostics-color=alwaysColorize compiler output when the terminal supports it
-fdiagnostics-show-optionShow the warning flag associated with a message
-fmax-errors=nStop after n errors

Terminal window
gcc -std=c17 -Wall -Wextra -Wpedantic -g -O0 main.c -o app
gdb ./app
OptionPurpose
-gGenerate debugger information
-ggdbGenerate debugging information tailored for GDB
-O0Turn off most optimization for straightforward stepping
-OgOptimize while preserving a useful debugging experience
-fno-omit-frame-pointerMake stack traces/profiling easier on many targets

For most active debugging, this is a useful balance:

Terminal window
gcc -std=c17 -Wall -Wextra -g3 -Og -fno-omit-frame-pointer main.c -o app
Terminal window
gdb ./app # Interactive debugger
gcc -E main.c | less # Inspect expanded macros/includes
gcc -S -fverbose-asm main.c # Assembly with compiler comments
gcc -H -c main.c # Print included header hierarchy

6. Finding Memory and Undefined-Behavior Bugs

Section titled “6. Finding Memory and Undefined-Behavior Bugs”

Sanitizers add runtime checks. Use them in testing and debugging builds, not as your usual optimized production binary.

Terminal window
gcc -std=c17 -Wall -Wextra -g -Og \
-fsanitize=address,undefined -fno-omit-frame-pointer \
main.c -o app
./app
SanitizerOptionDetects Examples
AddressSanitizer-fsanitize=addressOut-of-bounds access, use-after-free, some leaks
UndefinedBehaviorSanitizer-fsanitize=undefinedInvalid shifts, signed overflow, misaligned access
LeakSanitizer-fsanitize=leakHeap allocations not released
ThreadSanitizer-fsanitize=threadData races in multithreaded programs

Do not combine -fsanitize=thread with -fsanitize=address in the same executable. Sanitizer availability and behavior depend on the target platform and GCC installation.

Terminal window
gcc -std=c17 -Wall -Wextra -fanalyzer -c main.c

-fanalyzer explores paths through code to report issues such as double-free, null dereference, and some resource leaks. It can be slower and may need judgment when reviewing results.


project/
include/math_utils.h
src/main.c
src/math_utils.c
Terminal window
# One command: compile and link everything
gcc -std=c17 -Wall -Wextra -Iinclude \
src/main.c src/math_utils.c -o app
# Separate compilation: rebuild only changed files
gcc -std=c17 -Wall -Wextra -Iinclude -c src/main.c -o main.o
gcc -std=c17 -Wall -Wextra -Iinclude -c src/math_utils.c -o math_utils.o
gcc main.o math_utils.o -o app

When a header changes, its dependent source files must be rebuilt. GCC can generate dependency rules for make:

Terminal window
gcc -std=c17 -Wall -Wextra -MMD -MP -Iinclude -c src/main.c -o main.o
OptionPurpose
-MMDWrite a .d dependency file for user headers
-MPAdd dummy targets so removed headers do not break old dependency files
-MF fileChoose dependency output file
-MT targetChoose dependency rule target name

Terminal window
gcc main.c -lm -o app # Link libm for functions such as sqrt() on Unix-like systems
gcc main.c -pthread -o app # Compile/link POSIX threads where supported
OptionPurpose
-lNAMELink a library named like libNAME.so or libNAME.a
-LdirAdd a library search directory
-pthreadEnable and link thread support on compatible systems
-Wl,optionPass comma-separated option(s) to the linker

For traditional linkers, put libraries after object files that need them:

Terminal window
gcc main.o geometry.o -Llib -lshape -lm -o app
Terminal window
gcc -std=c17 -O2 -Iinclude -c src/math_utils.c -o math_utils.o
ar rcs libmathutils.a math_utils.o
gcc main.o -L. -lmathutils -o app

Static libraries are copied into the final executable as required during linking.

Terminal window
gcc -std=c17 -O2 -fPIC -Iinclude -c src/math_utils.c -o math_utils.o
gcc -shared math_utils.o -o libmathutils.so
gcc main.o -L. -lmathutils -Wl,-rpath,'$ORIGIN' -o app
OptionPurpose
-fPICGenerate position-independent code commonly required for shared libraries
-sharedProduce a shared library rather than an executable
-Wl,-rpath,...Embed a runtime library search path; use with care when packaging

Shared-library commands and filename conventions differ on Windows and macOS.


OptionTypical UseNotes
-O0Initial debuggingFast compile; easiest source-level stepping
-OgDebugging with reasonable performanceKeeps many debugging expectations intact
-O1Light optimizationSmaller optimization cost
-O2General release buildStrong default for many applications
-O3Benchmark-driven performance workCan increase size; measure results
-OsOptimize for sizeUseful for constrained binaries
-OfastAggressive performanceMay relax language/IEEE math guarantees
Terminal window
gcc -std=c17 -O2 -DNDEBUG -Wall -Wextra main.c -o app

-DNDEBUG disables the standard assert() macro. Use it only when removing runtime assertions is appropriate for the release.

Terminal window
gcc -std=c17 -O2 -flto -c main.c -o main.o
gcc -std=c17 -O2 -flto -c util.c -o util.o
gcc -O2 -flto main.o util.o -o app

-flto allows optimization across translation units. Use it consistently for compile and link steps, then measure compile time, binary size, and performance.

Terminal window
gcc -O2 -march=native main.c -o app

-march=native may use instructions available only on the build machine. It is useful for local performance tests, but usually wrong for binaries distributed to different computers.


Terminal window
gcc -std=c17 -O0 -g --coverage main.c -o app
./app
gcov main.c

--coverage instruments the program and produces files that gcov uses to show which source lines executed. Run representative tests before reading coverage.

Terminal window
# 1. Build an instrumented program
gcc -O2 -fprofile-generate main.c -o app
# 2. Run realistic workloads
./app input.txt
# 3. Rebuild using measured behavior
gcc -O2 -fprofile-use main.c -o app

PGO is useful only when the training runs resemble real use. Keep profile data aligned with the same source and build configuration.


Terminal window
gcc -DDEBUG -DLOG_LEVEL=2 main.c -o debug_app
gcc -DNDEBUG -DAPP_VERSION='"1.2.0"' main.c -o release_app
#if defined(DEBUG)
#define LOG(message) fprintf(stderr, "%s\n", message)
#else
#define LOG(message) ((void)0)
#endif
Terminal window
gcc -dM -E - < /dev/null # Unix shell: built-in C macros
gcc -std=c17 -dM -E - < /dev/null # Include selected standard mode

On PowerShell, a comparable empty input pipeline is:

Terminal window
'' | gcc -std=c17 -dM -E -
Terminal window
gcc -E -Iinclude src/main.c -o main.i
gcc -v -E -x c /dev/null # Print configured include search paths on Unix
gcc -H -Iinclude -c src/main.c # Trace headers being included

TaskCC++
Compile and linkgcc main.c -o appg++ main.cpp -o app
Standard-std=c17-std=c++20
Object compilegcc -c file.cg++ -c file.cpp
Link object files containing C++Prefer g++g++ *.o -o app

GCC can compile a .cpp file when invoked as gcc, but the final link step does not automatically add the C++ standard library. Use g++ for C++ executables:

Terminal window
g++ -std=c++20 main.cpp widget.cpp -o app
math_api.h
#ifdef __cplusplus
extern "C" {
#endif
int add(int a, int b);
#ifdef __cplusplus
}
#endif

extern "C" prevents C++ name mangling for APIs implemented in C.


For applications that accept untrusted input, compiler flags can add defense in depth. The suitable combination depends on target operating system, libc, deployment requirements, and performance budget.

Terminal window
gcc -std=c17 -O2 -Wall -Wextra \
-fstack-protector-strong -D_FORTIFY_SOURCE=2 \
-fPIE -pie main.c -o app
OptionPurpose
-fstack-protector-strongAdd stack corruption checks to more vulnerable functions
-D_FORTIFY_SOURCE=2Add some checked libc operations when optimization and libc support are available
-fPIE -pieBuild a position-independent executable for address randomization support
-Wl,-z,relro,-z,nowRequest additional ELF/Linux linker hardening

Sanitizers help find bugs during testing; hardening options help make some bugs harder to exploit in deployed builds. Neither replaces input validation, correct ownership, or testing.


Terminal window
gcc --help=warnings
gcc --help=optimizers
gcc --help=target
gcc -Q -O2 --help=optimizers

-Q -O2 --help=optimizers is useful for inspecting which optimization switches GCC enables for the selected compiler and target.

Terminal window
gcc -std=c17 -O2 -S main.c -o main.s
gcc -std=c17 -O2 -S -masm=intel main.c -o main.s # Supported on x86 targets
gcc -std=c17 -O2 -fverbose-asm -S main.c -o main.s

Symbols and Binary Information on Unix-like Systems

Section titled “Symbols and Binary Information on Unix-like Systems”
Terminal window
nm app # List symbols
objdump -d app # Disassemble
readelf -h app # ELF header information
ldd app # Shared library dependencies on Linux

These commands are provided by binutils or the operating system rather than by GCC itself.


CC := gcc
CPPFLAGS := -Iinclude
CFLAGS := -std=c17 -Wall -Wextra -Wpedantic -MMD -MP
LDLIBS :=
TARGET := app
SOURCES := src/main.c src/math_utils.c
OBJECTS := $(SOURCES:.c=.o)
DEPENDS := $(OBJECTS:.o=.d)
.PHONY: all debug release clean
all: debug
debug: CFLAGS += -g -Og -fsanitize=address,undefined -fno-omit-frame-pointer
debug: LDFLAGS += -fsanitize=address,undefined
debug: $(TARGET)
release: CFLAGS += -O2 -DNDEBUG
release: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
src/%.o: src/%.c
$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
clean:
rm -f $(TARGET) $(OBJECTS) $(DEPENDS)
-include $(DEPENDS)

Run:

Terminal window
make debug
make clean
make release

For larger projects, build into a separate directory so debug and release object files cannot be accidentally mixed.


Message or SymptomLikely CauseTypical Fix
fatal error: foo.h: No such file or directoryHeader search path missingAdd -Ipath/to/headers or fix include
undefined reference to 'foo'Declaration found, implementation not linkedAdd its .o file or -l library
undefined reference to 'sqrt'Math library not linked on Unix-like systemPut -lm after object/source files
multiple definition of 'x'Global definition placed in multiple files/headersPut extern declaration in header and one definition in a .c file
C++ undefined reference to std::...Linked C++ program using gccLink with g++
Program crashes only at runtimeMemory bug or undefined behaviorRebuild with -g -fsanitize=address,undefined
Debugger skips lines or variables vanishOptimization changes program layoutUse -Og or -O0 for debugging
Works locally, fails on another CPUBuilt with host-specific instructionsAvoid -march=native for distributed binaries
Terminal window
# Good: objects first, dependent libraries later
gcc main.o -L. -lutils -lm -o app
# Can fail with traditional static linking order
gcc -lutils -lm main.o -o app

Terminal window
gcc -std=c17 -Wall -Wextra -Wpedantic -Wconversion -g -Og main.c -o app
Terminal window
gcc -std=c17 -Wall -Wextra -g -Og \
-fsanitize=address,undefined -fno-omit-frame-pointer \
main.c -o app
Terminal window
gcc -std=c17 -Wall -Wextra -O2 -DNDEBUG main.c -o app
Terminal window
g++ -std=c++20 -Wall -Wextra -Wpedantic -g -Og main.cpp -o app
Terminal window
gcc -std=c17 -Wall -Wextra -Iinclude \
src/main.c src/io.c src/math_utils.c -o app
Terminal window
gcc -std=c17 -Wall -Wextra -Iinclude -c src/io.c -o io.o
gcc -std=c17 -Wall -Wextra -Iinclude -c src/main.c -o main.o
gcc main.o io.o -o app

  1. Compile one .c file with gcc file.c -o app.
  2. Always enable -Wall -Wextra and select a standard such as -std=c17.
  3. Learn what preprocessing, compilation, assembly, and linking do.
  4. Use -g -Og and a debugger instead of adding endless print statements.
  1. Split a program into headers and source files, compiling with -c.
  2. Link libraries with -L and -l, and understand link ordering.
  3. Add dependency generation using -MMD -MP in a Makefile.
  4. Run sanitizer builds regularly and fix every real diagnostic.
  1. Inspect generated assembly and optimization choices.
  2. Measure -O2, -O3, LTO, and PGO rather than assuming they are faster.
  3. Build shared/static libraries and control their public API.
  4. Add platform-appropriate hardening and automated warning/sanitizer builds.
Build C: gcc -std=c17 -Wall -Wextra file.c -o app
Build C++: g++ -std=c++20 -Wall -Wextra file.cpp -o app
Compile only: gcc -c file.c -o file.o
Link: gcc main.o util.o -lm -o app
Debug: gcc -g -Og file.c -o app
Sanitize: gcc -g -Og -fsanitize=address,undefined file.c -o app
Release: gcc -O2 -DNDEBUG file.c -o app
Preprocess: gcc -E file.c -o file.i
Assembly: gcc -O2 -S file.c -o file.s
Coverage: gcc --coverage file.c -o app && ./app && gcov file.c