Boring C++
Boring C++

Setup – Compiler, Editor, and Your First Program

10 min read

Chapter 2: Setup – Compiler, Editor, and Your First Program

Before you write a single line of C++, you need a working compiler and a place to type code. This chapter covers both. It also covers building your first program from the command line, which is a habit worth forming early — relying on an IDE's buttons to compile for you is fine for large projects, but understanding the underlying commands will save you hours of confusion down the line.

If you already have gcc or clang installed and you know your way around a terminal, skim this chapter. If you are starting from scratch, read every section — the troubleshooting section at the end is especially useful if something goes wrong on your machine.

2.1 Choosing a compiler

Three major compilers support modern C++:

GCC (the GNU Compiler Collection) — the standard compiler on Linux and available on macOS via Homebrew. Free, open source, excellent standards conformance. This book uses GCC in examples. Version 10 or later is required for good C++17 support.

Clang — Apple's default compiler on macOS. Also available on Linux. Produces better error messages than GCC in most cases, and is the compiler underlying Xcode on macOS. Version 12 or later for C++17.

MSVC (Microsoft Visual C++) — the compiler included with Visual Studio on Windows. Full C++17 support since Visual Studio 2019. If you are on Windows and not using WSL, MSVC is the path of least resistance.

All three produce standard C++ code. The code in this book compiles under all three. For the command-line examples we use g++ (the GCC C++ compiler), which is also what you have when you type g++ after installing GCC or the Apple Command Line Tools.

If you are on Windows: your simplest path is WSL 2 (Windows Subsystem for Linux), which gives you a full Linux environment inside Windows. The instructions below for Ubuntu apply directly. Alternatively, Visual Studio Community is free and gives you a full-featured IDE with MSVC.

2.2 Installing on Linux, macOS, and Windows

Linux (Ubuntu or Debian)

sudo apt update
sudo apt install build-essential cmake

build-essential installs GCC and the rest of the GNU toolchain. cmake is the build system we use in Section 2.6.

Verify the installation:

g++ --version
cmake --version

You should see something like g++ (Ubuntu 12.3.0-1ubuntu1~22.04) 12.3.0. The version number should be 10 or higher.

macOS

Apple ships a version of Clang through the Command Line Tools package. Install it with:

xcode-select --install

A dialog will appear asking you to install the tools. Click Install. This may take several minutes.

After installation:

g++ --version

On macOS, g++ is an alias for Apple Clang. The version output will say something like Apple clang version 15.0.0. This is fine — Apple Clang is fully compatible with C++17.

For CMake:

brew install cmake

If you do not have Homebrew installed, follow the instructions at brew.sh.

Windows

Option 1: WSL 2 (recommended for this book)

Enable WSL 2 in PowerShell (run as Administrator):

wsl --install

This installs Ubuntu by default. After the system restarts, open Ubuntu from the Start menu and follow the Ubuntu instructions above.

Option 2: Visual Studio Community

Download Visual Studio Community (free) from visualstudio.microsoft.com. During installation, select the Desktop development with C++ workload. This installs MSVC and CMake.

To use the command line with MSVC, open the Developer Command Prompt for VS (search for it in the Start menu) and use cl instead of g++.

2.3 Your editor

Any editor that handles plain text works. The most popular choice for C++ development in 2026 is Visual Studio Code (VS Code), which is free and runs on all platforms.

Install two extensions in VS Code:

  1. C/C++ (publisher: Microsoft) — provides syntax highlighting, IntelliSense, and debugging.
  2. CMake Tools (publisher: Microsoft) — integrates CMake with VS Code.

Open a folder with your source files and VS Code will detect the project structure automatically once CMake is configured.

We will not use any IDE-specific features in this book. Every step can be done from the command line, and every listing can be opened in any editor. Use what makes you comfortable.

2.4 Your first C++ program: Hello, world

Create a file called hello.cpp with the following content:

#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

Every single piece of this program matters, so let us go through it line by line.

#include <iostream>
This is a preprocessor directive. Before the compiler sees your code, a preprocessor reads it and processes lines that start with #. #include <iostream> tells it to paste the contents of the iostream header file — a file provided by the C++ standard library — into your source. iostream provides std::cout, which you are about to use. Without this line, the compiler would not know what std::cout is.

In C++, you include what you need, explicitly. There is no automatic import of everything. This is verbose at first, but it means you always know what your code depends on.

int main()
Every C++ program starts here. main is a special function — the entry point. When you run your program, the operating system calls main. It takes no arguments in this version (there is a version that takes command-line arguments, covered in Chapter 12). The int before main means it returns an integer.

std::cout << "Hello, world!" << std::endl;
std::cout is the standard output stream — conceptually, the terminal's output. The << operator sends data to the stream. You can chain multiple << operators on the same line, which is what we do here: we send the string "Hello, world!" and then std::endl.

std::endl flushes the stream and appends a newline character. The std:: prefix means cout and endl live in the std namespace — a grouping mechanism that prevents name collisions. Most of the standard library lives in std::.

A common alternative to std::endl is "\n" (a newline character). The difference: std::endl flushes the output buffer (writes everything immediately), while "\n" does not flush. For most programs the difference does not matter. For programs that print a lot in a tight loop, prefer "\n" — flushing on every output line is slow.

return 0;
main returns an int. By convention, returning 0 means "success." Returning a nonzero value signals an error to the operating system. Tools like shell scripts and CI pipelines check this return value. Get in the habit of returning 0 on success.

Compiling and running

In a terminal, navigate to the folder containing hello.cpp and run:

g++ -std=c++17 -Wall -Wextra -o hello hello.cpp

Then run it:

./hello

You should see:

Hello, world!

2.5 The build command explained

Let us break down the compile command:

g++  -std=c++17  -Wall  -Wextra  -o hello  hello.cpp

g++ — the GNU C++ compiler. It compiles .cpp files (C++ source) and links them into an executable.

-std=c++17 — use the C++17 standard. Without this flag, the compiler defaults to an older standard, and some features in this book will not compile. Always include this flag. If you want C++20 features, use -std=c++20.

-Wall — enable "all" common warnings. The name is historical; it is not literally all warnings. But it catches many common mistakes: uninitialized variables, suspicious comparisons, missing return statements. Always compile with -Wall.

-Wextra — enable additional warnings beyond -Wall. Together, -Wall -Wextra catch the vast majority of mistakes beginners make. Treat warnings as errors until you understand why a specific warning is safe to ignore.

-o hello — name the output file hello. Without this, g++ names the output a.out by default, which is unhelpful.

hello.cpp — the source file to compile. If you have multiple source files, list them all here (or use CMake, covered next).

When you run g++, several things happen internally:

  1. Preprocessing: directives like #include are processed. The contents of iostream are pasted in.
  2. Compilation: the preprocessed C++ source is compiled to assembly code.
  3. Assembly: the assembler converts assembly to object code (a .o file).
  4. Linking: the linker combines your object code with the standard library code to produce the final executable.

For a single-file program, this happens invisibly in one command. For multi-file projects, you need a build system — which is what CMake provides.

2.6 CMake: the boring minimum

For single-file programs, g++ -std=c++17 -Wall -Wextra -o program source.cpp is all you need. Once you have more than one source file, or you want dependencies, or you want your project to build on multiple platforms, you need a build system.

The most widely used C++ build system is CMake. It generates platform-specific build files (Makefiles on Linux, Xcode projects on macOS, Visual Studio projects on Windows) from a single cross-platform description.

Here is the minimal CMakeLists.txt for a single-executable project:

cmake_minimum_required(VERSION 3.16)
project(hello)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(hello hello.cpp)

Save this as CMakeLists.txt in the same folder as hello.cpp. Then build:

mkdir build
cd build
cmake ..
cmake --build .
./hello
  • mkdir build — create a directory for build artifacts. Always build out-of-source (in a separate directory) so the generated files do not clutter your source.
  • cmake .. — run CMake, pointing it to the parent directory (where CMakeLists.txt lives). CMake generates the build files.
  • cmake --build . — run the actual build. CMake invokes your platform's build tool (make, ninja, MSBuild, etc.) for you.

To add more source files:

add_executable(hello
    hello.cpp
    utils.cpp
    parser.cpp
)

To add compiler flags:

target_compile_options(hello PRIVATE -Wall -Wextra)

That is all the CMake you need for this book. Chapter 12 adds a few more features when we build the complete project.

2.7 Common installation problems and fixes

g++: command not found
The compiler is not installed, or it is not on your PATH. On Ubuntu: run sudo apt install build-essential. On macOS: run xcode-select --install. On Windows: use WSL or install Visual Studio.

The compiler version is too old
Run g++ --version. If the version is below 10 (for GCC) or 12 (for Clang), upgrade. On Ubuntu: sudo apt install gcc-12 g++-12. Use g++-12 instead of g++ until you update your default.

cmake: command not found
CMake is not installed. On Ubuntu: sudo apt install cmake. On macOS: brew install cmake.

CMake cannot find the compiler
On some systems CMake needs to be told which compiler to use:

cmake -DCMAKE_CXX_COMPILER=g++ ..

#include <iostream> not found (macOS)
The Command Line Tools are not installed or need to be updated. Run xcode-select --install and try again.

Permission denied when running the executable
The output file is not executable. Fix with chmod +x hello, or check that you ran the compile command in a directory you own.

Windows line endings causing strange compile errors
If you edit files on Windows and compile on Linux (via WSL), CRLF line endings can confuse the compiler in edge cases. Configure git: git config --global core.autocrlf input. For files already affected, use dos2unix file.cpp.

#include <bits/stdc++.h> not found
This header is a GCC-specific extension that includes everything in the standard library at once. It is popular in competitive programming but is not portable — it does not exist on Clang or MSVC. Do not use it. Include only what you need: <iostream>, <vector>, <string>, etc.

Linker error: undefined reference to pthread_...
Concurrency code (Chapter 10) requires linking against the POSIX threads library on Linux. Add -pthread to your compile command: g++ -std=c++17 -Wall -Wextra -pthread -o program source.cpp. In CMake: target_link_libraries(program PRIVATE pthread).

2.8 Summary

You now have everything you need to write, compile, and run C++ programs:

  • A compiler (g++, clang, or MSVC) that supports C++17.
  • A build command: g++ -std=c++17 -Wall -Wextra -o output source.cpp.
  • A minimal CMake setup for multi-file projects.
  • An editor of your choice.

The compile-run-fix loop — compile, see the error, fix it, compile again — is the fundamental rhythm of working in C++. The compiler is not your adversary; it is telling you something useful. Learning to read its output quickly is a skill that comes with practice.

In Chapter 3 we start the actual language: types, variables, and control flow. From here, things move at a steady pace.

All code from this chapter is in the GitHub repository:
<TODO: book code repository URL>/tree/main/chapter02

Boring C++

Modern C++ Mastery for Programmers Who Prefer Clarity Over Cleverness.

View book →