Compiling and running C++ from the command line is a two-step ritual you will perform hundreds of times: translate the source file into an executable with the compiler, then execute that executable. Once the exact command is muscle memory you stop thinking about the toolchain entirely and start thinking about your program.
The two commands that matter
Everything about compiling and running C++ from the command line fits into one line for a single-file program.
g++ -std=c++17 -Wall -Wextra main.cpp -o program
./programThe first command compiles. The second runs. On Windows the second becomes program.exe, and on the Windows Command Prompt it is just program.
Breaking the compile command down piece by piece:
g++— the driver that runs the preprocessor, compiler, assembler and linker for you. Using theg++driver rather than the rawgccis what makes C++ standard library headers work correctly.-std=c++17— the language standard. C++17 is the safe default for everything in this tutorial.-Wall -Wextra— enable warnings. These are advice from the compiler about code that is legal but probably wrong.main.cpp— the source file.-o program— the name of the executable that comes out. Without it the binary is calleda.outon Linux and macOS.
The order of those flags does not matter to the compiler, but keeping them in this order makes your commands easy to read at a glance.
Watching the compiler work
Add -v and the compiler narrates every step it takes, which is the fastest way to understand what compiling actually involves.
g++ -std=c++17 -Wall -Wextra -v main.cpp -o program 2>&1 | head -40You will see the search paths for headers, the invocation of the preprocessor, the compiler proper, the assembler and finally the linker. Notice how many separate programs are involved. The linker is the last step and it is the one that reports the most common beginner error: an , which means a function was called but its definition was never handed to the linker.undefined reference
The program the commands refer to
Every command in this lesson compiles the same small program, so that the flags can be compared against a known result.
#include
int main() {
double celsius = 21.5;
std::cout << "fahrenheit: " << celsius * 9.0 / 5.0 + 32.0 << '\n';
return 0;
}Building it with the flags from this lesson produces a binary that prints 70.7. Adding -Wall changes nothing about the output, but it tells you about the problems the compiler is able to see on its own.
One program, two files
Real projects are split across files, and the command line for that is a list of source files rather than one. The header below holds the shared interface; the implementation and the entry point live in a second file.
#include
// greeting.hpp — the interface shared by the two files
std::string greeting_for(const std::string& name);The second file includes the header, defines the function and provides main.
#include
#include
// greeting.cpp — the implementation
std::string greeting_for(const std::string& name) {
return "Hello, " + name + "!";
}
// main.cpp — the entry point
int main() {
std::cout << greeting_for("world") << '\n';
return 0;
}The output is the familiar greeting:
Hello, world!Building the two files together is a single command that names both source files:
g++ -std=c++17 -Wall -Wextra greeting.cpp main.cpp -o greeting
./greetingCommon compiler errors you will meet today
Three messages account for most first-day failures.
error: 'cout' was not declared in this scope
error: expected ';' before '}' token
undefined reference to 'foo'The first usually means a missing #include <iostream> or a missing std:: prefix. The second is almost always a missing semicolon. The third means a function is declared or called but never defined in any file that was compiled and linked together.
Reading a compiler error takes practice, and the trick is to read it bottom-up. The first error is usually the real one; everything after it is often a cascade caused by the parser losing its place.
Rebuilding after every edit
Compilation takes time, and you will make many small edits. The classic loop is: edit, compile, run. The mistake is running the previous binary and wondering why your change did nothing.
Two habits prevent that entirely. First, put the compile and run steps in one command so the build can never be skipped.
g++ -std=c++17 -Wall -Wextra main.cpp -o program && ./programThe && means the program only runs if the compile succeeded. Second, turn on the dependency checker and let the build tool rebuild only what changed.
g++ -std=c++17 -Wall -Wextra -MMD -MP main.cpp -o programThat adds a dependency file next to the object file, which is what build tools such as make and CMake use to decide what needs recompiling. You do not need to know the details yet; the point is that a manual one-file workflow and an automatic build workflow agree on what changed.
Where the output file goes
The -o flag controls the output path and directory. If you omit it, the binary lands in the current directory under a default name, which is a.out on Linux and macOS.
# put the binary somewhere specific
g++ -std=c++17 main.cpp -o build/program
# create the folder first, or the compiler will complain
mkdir -p build && g++ -std=c++17 main.cpp -o build/programOn Windows the compiler appends .exe automatically, so -o program produces program.exe. Passing -o program.exe explicitly also works and makes shell scripts portable between the two worlds.
Sanitizers turn on the bug detectors
The single highest-value flag for a beginner is the address sanitizer. It instruments the compiled code so that a mistake which would otherwise corrupt memory or read out of bounds is caught immediately, with a message that names the line.
g++ -std=c++17 -Wall -Wextra -g -fsanitize=address,undefined main.cpp -o program
./programAdd -g so the debugger can map the report back to source lines. The sanitizer catches a second family of problems: signed integer overflow, misaligned access and other operations the C++ standard says are undefined behaviourundefined rather than merely wrong. Together these two tools catch most of the bugs that would otherwise appear as random crashes hours later.
Quick reference
- Compile with
g++ -std=c++17 -Wall -Wextra file.cpp -o name, then run./name. -onames the output; omitting it givesa.out.- Read compiler errors bottom-up and fix the first one only.
- Chain compile and run with
&&so you never run a stale binary. - Add
-g -fsanitize=address,undefinedwhile learning; it finds bugs at the exact line. - On Windows the binary is
name.exeand runs asnameor.\name.
Try it yourself: compile the hello world program, then introduce three deliberate mistakes one at a time — a missing semicolon, a missing [[#include]] and a call to a function you never defined. For each, record the exact error text and the line the compiler pointed at. Then rebuild with the sanitizers enabled and confirm a clean run.