Article by Ayman Alheraki on January 11 2026 10:36 AM
Before diving into actual GCC commands, it's important to understand how a C++ program is compiled. The compilation process consists of four main stages:
cpp)This step expands macros, includes header files, and removes comments.
Any file with #include, #define, or #pragma is affected during preprocessing.
You can inspect the result of this stage using:
xg++ -E main.cpp -o main.i
This generates a preprocessed file (main.i) with all macros expanded and headers included.
cc1plus)Converts preprocessed code into assembly language.
This step ensures the program is syntactically correct and generates .s (assembly) files.
Run:
xg++ -S main.i -o main.s
This outputs assembly code for main.cpp.
as)Translates assembly instructions into machine code, creating object files (.o).
Run:
xg++ -c main.s -o main.o
This step produces main.o, which is not yet an executable.
ld)Combines all object files (.o) and necessary libraries into a final executable.
Run:
xg++ main.o -o my_program
The final output is the executable binary my_program.
xg++ -o my_program main.cpp
-o my_program: Specifies the output file name.
main.cpp: Input source file.
xg++ -c main.cpp -o main.o
The -c flag tells GCC to compile only, producing an .o file instead of an executable.
xg++ main.o utils.o -o my_program
This command links multiple object files into a single executable.
In larger projects, splitting code into multiple files improves maintainability.
x/project├── main.cpp├── utils.cpp├── utils.h├── Makefile (optional)
xg++ -c main.cpp -o main.og++ -c utils.cpp -o utils.o
xg++ main.o utils.o -o my_program
Why separate compilation?
Faster rebuilds: Only modified files are recompiled.
Better organization: Code is split into logical units (header files, source files).
GCC offers various optimization flags:
| Flag | Description |
|---|---|
-O0 | No optimization (default). Best for debugging. |
-O1 | Basic optimizations. |
-O2 | More aggressive optimizations (recommended for production). |
-O3 | Maximum optimization, but may increase binary size. |
-Os | Optimizes for smaller executable size. |
-Ofast | Extreme optimizations (unsafe for some cases). |
Example Usage:
xg++ -O2 -o my_program main.cpp
To include debug information, use -g:
xg++ -g -o my_program main.cpp
Then debug using GDB:
xgdb ./my_program
Enable useful warnings to catch potential issues:
xg++ -Wall -Wextra -Wpedantic -o my_program main.cpp
xg++ -Werror -o my_program main.cpp
This forces you to fix warnings before proceeding.
.a)xg++ -c mylib.cpp -o mylib.oar rcs libmylib.a mylib.o
To link:
xg++ main.cpp -L. -lmylib -o my_program
.so)xg++ -fPIC -shared -o libmylib.so mylib.cpp
To link:
xg++ main.cpp -L. -lmylib -o my_program
To run:
xLD_LIBRARY_PATH=. ./my_program
Without a build system, you must:
Use #pragma once or include guards in headers.
Specify include paths:
xg++ -I./include -c main.cpp
Specify library paths:
xg++ -L./libs -lmylib -o my_program
build.sh)x#!/bin/bashg++ -c main.cpp -o main.og++ -c utils.cpp -o utils.og++ main.o utils.o -o my_programecho "Build completed!"
xchmod +x build.sh./build.sh
Use a logical project structure (src/, include/, lib/).
Enable compiler warnings and treat them as errors (-Wall -Werror).
Optimize for performance using -O2 or -O3.
Use -g during development for debugging support.
Automate builds with scripts to avoid long commands.
Consider using Makefiles for medium-sized projects.
By following this guide, you can efficiently build and manage C++ projects without relying on a build system. This approach offers fine-grained control, but it requires manual effort to manage dependencies and optimizations.