Here are some of the useful, but not widely known, options of GCC I use periodically.

Output preprocessor output, assembly and object code

gcc -save-temp=obj -o hello main.cpp

outputs the following in addition to hello

  1. hello-main.ii: annotated output of preprocessor
  2. hello-main.s: assembly
  3. hello-main.o: object code

GCC Build Specs

gcc -dumpspecs

Gives the specifications with which GCC was built.

List supported targets

gcc --target-help
gcc --help=target

Clang has clang -print-supported-cpus and clang -print-targets similarly. Though Clang operates on target triples/quadruples.

On older GCC alternate command is gcc -E -march=help -xc /dev/null.

Stack Usage

gcc -fstack-usage

Gives the stack usage function-wise for the compiled translation unit; this is helpful in measuring runtime memory usage. See the manual for details on deciphering its output.

Macros and Include Directories

# get macros defined when the language is C++
cpp -xc++ -dM /dev/null

# get macros defined when the language is C
cpp -dM /dev/null

# print built-in search directories for binaries and libraries but not includes
gcc -print-search-dirs

Prints the macros defined when the preprocessor was called. Using GCC instead of CPP is popular as gcc -dM -E - << /dev/null but is not as good for two reasons:

  1. If you want to talk to the preprocessor, talk directly to it, why go through the compiler?
  2. Looking at this, one might get a false hope that g++ -dM -E - << /dev/null will spit C++ macros; it doesn’t. Instead one has to do g++ -xc++ -dM -E - << /dev/null for some reasons.

For setting up auto-completion in Emacs I needed to know the include directories GCC searches. How do I find them?

cpp -xc++ -Wp,-v /dev/null

This prints the list of standard include directories. g++ -v some.cpp would give it too, but this is fast & easy; no input file or fake compiler calls. Another similar option is g++ -### some.cpp.


g++ -std=c++14 -O3 -c -masm=intel -fverbose-asm -Wa,-adhln=prgm.s prgm.cpp

will show the disassembly of a compilation unit in Intel syntax with inter-weaved source listing. This one is very popular among optimisation enthusiasts 😃 Here’s one more for them.

Class Memory Layout

g++ -fdump-class-hierarchy my_classes.cpp

This would show object memory layout of classes in the source; includes classes with complex inheritance hierarchies. Padding can needlessly increase the size of a structure; use -Wpadded to reveal when padding bits are added.

Optimization Result

From GCC 9 onwards you can check if an optimization was performed or missed (-fopt-info). Example

$ g++ -c -O2 -fopt-info-inline-all note: Considering inline candidate void foreach(T, T, void (*)(E)) [with T = char**; E = char*]/2. optimized:  Inlining void foreach(T, T, void (*)(E)) [with T = char**; E = char*]/2 into int main(int, char**)/1. missed:   not inlinable: void inline_me(char*)/0 -> int std::puts(const char*)/3, function body not available optimized:  Inlined void inline_me(char*)/4 into int main(int, char**)/1 which now has time 127.363637 and size 11, net change of +0.
Unit growth for small function inlining: 16->16 (0%)

Inlined 2 calls, eliminated 1 functions


Use -ftree-vectorize to enable vectorization of loops; this is implicitly enabled at -O3.


A language as low-level and powerful C++ is comes with its own set of challenges. Many tools have been built over the years to help programmers; sanitizers are an important set of tools GCC has (-fsanitize). Some interesting ones

  • Address sanitizer (-fsanitize=address -static-libasan)
  • Leak sanitizer (-fsanitize=leak -static-liblsan)
  • Thread sanitizer (-fsanitize=thread -static-libtsan)
  • Undefined Behaviour sanitizer (-fsanitize=undefined -static-libubsan)

Related: Gavin’s blog post.

Header Dependency Tree

There’re times when multiple headers with interlinked dependencies are a problem. It’d be easier to understand why a definition is deemed missing by the compiler, despite including a header you think should’ve made it visible. These preprocessor options are your friends:

  • -M show dependencies for all headers
  • -MM show dependencies for non-system headers
  • -H show dependencies as a hierarchy
> g++ -Wall -stc=c++17 -pedantic -H test.cpp

. //XcodeDefault.xctoolchain/usr/include/c++/v1/cmath
.. //XcodeDefault.xctoolchain/usr/include/c++/v1/__config
.. //XcodeDefault.xctoolchain/usr/include/c++/v1/math.h
... //XcodeDefault.xctoolchain/usr/include/c++/v1/__config
... //MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/math.h

Old compilers + new flags

GCC doesn’t seem to have this feature yet.

When an unknown flag is passed to Clang, it’ll raise a warning by default; projects often use -Werror to promote warnings to errors. If using a new Clang-16-introduced flag (e.g. -Wno-enum-constexpr-conversion), unfamiliar to older versions, but still want the project be build-able using older compiler versions, use -Wno-unknown-warning-option. Older versions will silently ignore the unknown (but new) flag. Newer versions will recognize and honour both.

A small caveat: using this flag means even really incorrect flags passed to Clang will go unwarned. Perhaps using this for the short run is a better middle ground.

What interesting GCC options do you know?