C++ FOUNDATIONS · 17 MIN STUDY

From C++ source code to an executable

A C++ toolchain checks and translates source text, then connects the required pieces into a program the operating system can start. “Compile” is often used casually for the whole process; this lesson separates the steps.

Learning objectives

  • Name the practical stages preprocessing, compilation, assembly, and linking.
  • Distinguish source files, object files, libraries, and executables.
  • Read a basic g++ build command without treating it as universal.

Prerequisites: none. This is an early foundation lesson. You only need to recognize that source code is text saved in a file.

Why a toolchain exists

You write C++ as text in a source file, commonly with a .cpp extension. The text mentions names such as std::cout and can include declarations from other files. A target computer needs machine-level code and connected support libraries. A toolchain is the collection of tools that performs this work.

Different compilers and operating systems package the process differently. The stages below are a useful model and match GCC’s documented compilation stages, but a particular toolchain may run multiple stages in one command or keep intermediate files hidden.

hello.cpp (source text)
       ↓ preprocessing
expanded source / translation input
       ↓ compilation proper
assembly representation
       ↓ assembly
hello.o (object file)
       ↓ linking with required libraries and startup code
hello or hello.exe (runnable program)

Stage 1: preprocessing

Before ordinary C++ parsing, the preprocessor handles directives such as #include. For a simple program, #include <iostream> makes declarations from that header available to the source being translated. The preprocessor also handles conditional compilation directives and macros. It does not “run the program.”

Stage 2: compilation proper

The compiler analyzes C++ syntax and rules, associates names with declarations, checks many type-related constraints, and transforms the program into a lower-level representation. A diagnostic here can point to an error such as a missing name, malformed statement, or invalid use of a type. A successful compile does not prove that your chosen algorithm is correct.

Stage 3: assembly

An assembler converts assembly representation into an object file. An object file is not normally a complete application. It commonly contains machine code plus information that lets a linker connect names defined in other files or libraries. Its exact format depends on the target platform.

Stage 4: linking

A linker combines object files with required libraries and startup components, resolving references between them. In this lesson’s program, the output operation needs standard-library support. If the linker cannot find a required definition, it reports an unresolved-reference error. Linking is different from a C++ syntax error: the source may have compiled, but the final program still cannot be built.

Compile a small program

C++ — hello.cpp
#include <iostream>

int main() {
    std::cout << "Build complete.\n";
}
TERMINAL — ONE GCC/MinGW EXAMPLE
g++ -std=c++17 -Wall -Wextra -pedantic hello.cpp -o hello

Code and command, line by line

  1. #include <iostream> requests declarations for standard stream input/output.
  2. int main() defines the normal entry function of this hosted C++ program.
  3. The output statement sends a string and then a newline to standard output.
  4. g++ invokes GCC’s C++ driver. Another toolchain uses a different command.
  5. -std=c++17 selects the C++17 language mode for this build.
  6. -Wall -Wextra request useful warning groups; warnings deserve investigation even when a build finishes.
  7. -pedantic asks GCC for diagnostics required by the chosen standard mode where applicable; it does not make every program defect impossible.
  8. hello.cpp is the input source file. -o hello chooses the output program name.
Expected output after running the resulting program Build complete.

What happens when this program is built and run

  1. The driver recognizes .cpp as C++ source and runs the needed translation stages.
  2. The preprocessor makes the included declarations available; the compiler checks and translates the C++ program.
  3. The assembler creates object code; the linker connects it with appropriate support.
  4. When you launch the resulting executable, the operating system creates a process and starts the program’s startup sequence.
  5. For this ordinary hosted program, execution reaches main; the output statement is evaluated and the program ends.
Accuracy note: the C++ standard defines translation phases and language behavior, not a required set of separate executable tools. “Preprocessor, compiler, assembler, linker” describes the familiar practical pipeline used by toolchains such as GCC.

Common beginner mistakes

  • Running the source file: a .cpp file is input to a C++ toolchain, not usually a directly runnable program.
  • Calling every error a compiler error: an error can occur during preprocessing, compilation, assembly, linking, or later while running.
  • Ignoring warnings: warnings are not always fatal, but they often reveal a bug or a misunderstood conversion.
  • Copying a command blindly: commands, paths, output extensions, and installation steps vary by toolchain and operating system.
  • Assuming the executable is portable by itself: an executable is normally built for a particular target platform and environment.

Practical exercises

  1. Draw the four-stage diagram from memory and label which stage produces an object file.
  2. Save the example as hello.cpp. Build it with your installed compiler and record the exact command that worked on your computer.
  3. Intentionally misspell std::cout as std::coutt. Read the diagnostic, restore the spelling, and rebuild.
  4. Build two source files that each define the same non-inline function name. Observe that source translation can succeed while linking reports a conflict; then remove one definition.
Show worked exercise solutions
  1. The practical pipeline is preprocessing → compilation proper → assembly → linking. Assembly commonly produces an object file; linking combines object files and required libraries into an executable.
  2. One GCC/MinGW example is g++ -std=c++17 -Wall -Wextra -pedantic hello.cpp -o hello. Your exact command and executable name may differ by compiler and operating system.
  3. The compiler reports that std::coutt is not declared (the exact wording varies). Restore std::cout and rebuild.
  4. For example, put int value() { return 1; } in a.cpp and int value() { return 2; } in b.cpp, with main in only one file. Compile and link both files together. Each translation unit can compile, but the linker finds two external definitions of value. Keep one definition and, if needed, place its declaration in a header included by both files.

Assessment: check your understanding

1. Which stage combines object files and libraries into a runnable program?

Answer: Linking. Why: The linker resolves references among separately produced object files and library code.

2. Is an object file normally the same as a complete executable?

Answer: No. Why: It can contain unresolved references and is often one input to the link step.

3. What does #include <iostream> do at a beginner-appropriate level?

Answer: It makes declarations from the standard input/output header available during translation. Why: The program needs those declarations to use names such as std::cout.

4. Does successful compilation prove the program solves the intended problem?

Answer: No. Why: Translation checks many language rules, but it cannot generally know whether your chosen formula or requirement was the intended one.

5. Why might g++ use one command for several stages?

Answer: It is a driver that coordinates the needed stages. Why: Toolchains commonly hide intermediate files unless options request them.

Revision summary

C++ source passes through a practical pipeline: preprocessing, compilation proper, assembly, and linking. The toolchain can package those stages into one command, but they solve different jobs. An executable is the linked result that an operating system can start; the next lesson follows what happens after that start.

Knowledge check

  1. Which stage resolves references to definitions in other object files or libraries?
  2. Why is an object file not necessarily a runnable program?
  3. Does a successful compile prove the algorithm is correct?
Show reasoned answers
  1. Linking.
  2. It may still contain unresolved references and lack required startup or library components.
  3. No. Compilation checks language and translation constraints, not the intended algorithmic result.

References