g++ -c: compile C++ to an object file without linking
- tool
- C++
- command
- g++
- flag
- -c
- default
- off: g++ compiles, assembles and links into an executable (a.out)
g++ -c file.cpp stops after compiling and assembling: it writes an object file (file.o, or file.obj with MSVC-style toolchains) and does not link, so the file does not need a main function and undefined functions are not an error yet. The objects are combined later with g++ a.o b.o -o app. This is how every build system compiles C++: one -c step per source file, then one link step. Clang (clang++ -c) behaves the same.
Separate compilation, then link
The example uses Clang 20 from the Zig 0.15.1 toolchain (installed from PyPI with uv); cxx below is that compiler, and g++ accepts the same options:
cxx() { uv run -q --no-project --python 3.13 --with ziglang==0.15.1 python -m ziglang c++ -fno-color-diagnostics "$@"; }
cat > greet.h <<'EOF'
#pragma once
#include <string>
std::string greet(const std::string& name);
EOF
cat > greet.cpp <<'EOF'
#include "greet.h"
std::string greet(const std::string& name) { return "hello " + name; }
EOF
cat > main.cpp <<'EOF'
#include <iostream>
#include "greet.h"
int main() { std::cout << greet("c++") << '\n'; }
EOF
cxx -std=c++17 -Wall -c greet.cpp -o greet.o
cxx -std=c++17 -Wall -c main.cpp -o main.o
ls *.o
cxx main.o greet.o -o app && ./app
echo "== change greet.cpp: recompile one file, relink"
sed -i 's/hello /hi /' greet.cpp
cxx -std=c++17 -Wall -c greet.cpp -o greet.o
cxx main.o greet.o -o app2 && ./app2Output
greet.o main.o hello c++ == change greet.cpp: recompile one file, relink hi c++
greet.cpp has no main, which is fine with -c. After editing it, only greet.o is rebuilt; main.o is reused in the link. Build tools such as Make, Ninja and CMake automate exactly this, based on file timestamps and header dependencies.
Related options
| Option | Stops after | Output |
|---|---|---|
| -E | Preprocessing | Preprocessed source on stdout |
| -S | Compilation | Assembly file (.s) |
| -c | Assembling | Object file (.o) |
| (none) | Linking | Executable (a.out or the -o name) |
| -o file | - | Name of the output of the last stage |
| -MMD -MP | - | Also write a .d dependency file for Make, listing the headers |
With GCC, g++ -c a.cpp b.cpp writes a.o and b.o in one call, and -o is then not allowed (cannot specify '-o' with '-c', '-S' or '-E' with multiple files). The Zig-packaged Clang used above, on Windows, rejects several files with -c altogether (coff does not support linking multiple objects into one), so the example compiles one file per call, which is what build systems do anyway.
Linking problems after -c
- Undefined reference / undefined symbol: a function is declared in a header but its object file or library is missing from the link command. Add the
.oor the-lnameoption; see undefined reference to. - Linking with gcc instead of g++:
gcc main.o greet.odoes not add the C++ standard library, which produces undefined references tostd::symbols. Useg++(orclang++) for the link step. - Order of libraries: with GNU ld,
-lfoomust come after the objects that use it:g++ main.o -lfoo, notg++ -lfoo main.o. - Mismatched flags: objects compiled with different
-stdlevels usually link fine, but different ABI options (-D_GLIBCXX_USE_CXX11_ABI,-fsanitize,-m32) can cause link or run-time errors.
For one-command builds of small programs, see compile a C++ program.
FAQ
Why compile with -c instead of all files at once?
Separate compilation lets a build tool recompile only the files that changed and then relink. For a large project this turns minutes into seconds. It also allows mixing objects from different compilers or languages, such as C and C++, in one link step.
Do I need the same flags for compiling and linking?
Most -c flags (warnings, -I, -D) matter only when compiling. Options that change the ABI or runtime must be given in both steps: -pthread, -fsanitize=address, -static, -flto and -m32/-m64. Linking is best done with g++ (not gcc or ld) so the C++ standard library is added.
What is the difference between a .o file and a .a or .so?
A .o is one compiled translation unit. A static library (.a) is an archive of .o files made with ar, and a shared library (.so, .dylib, .dll) is a linked file loaded at run time. All three can be passed to the final g++ link command.