Chapter 01 of 24 | C++

What C++ is and how a program runs

C++ is a compiled, statically typed language: source code is checked and translated into machine instructions before the program runs. It is used in areas ranging from games and desktop applications to embedded and scientific software. The language gives you control over performance and representation, while its standard library supplies safer, reusable tools.

From source to running program

A typical build has several stages. The preprocessor handles directives such as #include; the compiler parses and type-checks each translation unit and produces object code; the linker combines object files and libraries into an executable. A diagnostic may come from any of these stages, so notice whether it reports a source line, an unresolved symbol, or a runtime failure.

source.cpp  →  compiler  →  object file  →  linker  →  executable

A compiler is not the C++ language itself. GCC, Clang, and Microsoft Visual C++ are different implementations, and support for new standard features can differ by version. This guide uses C++23 where useful; check your compiler's feature support and use the newest standard mode it implements reliably.

Why are there so many stages?

Large programs are divided into translation units so different source files can be compiled separately. The preprocessor runs on each source file, then the compiler translates that translation unit. Later, the linker joins the resulting object files and libraries. This separation makes incremental builds possible: when one implementation file changes, a build system may only need to rebuild that file and relink the program.

For example, main.cpp may call a function declared in greeting.hpp and defined in greeting.cpp. Compiling main.cpp only requires the declaration so it can check the call. The actual function body is provided later by compiling greeting.cpp and linking both object files. If that implementation file is omitted, compilation can succeed but linking cannot find the function's definition.

This also explains why errors that look similar are not interchangeable. A missing semicolon is a syntax error caught by the compiler. An attempt to call a function with the wrong argument type is normally a type-checking error. A declaration with no linked definition is a linker error. A program that builds but calculates the wrong answer has a logic error. Identifying the stage gives you the right place to investigate.

What does "compiled" mean?

Compilation does not make a program automatically correct. The compiler checks that your source follows language rules and translates it, but it usually cannot know whether your algorithm matches your goal. A program can be perfectly valid C++ and still use the wrong formula, misunderstand its input, or access an object after its lifetime ends.

Also distinguish a language rule from an implementation choice. The C++ standard specifies the behavior a conforming program may rely on. A compiler implements the standard for particular operating systems and processor targets. It may add extensions, but code that depends on those extensions may not compile elsewhere. Portable programs rely on guarantees in the standard rather than incidental behavior observed on one machine.

A repeatable first build

Build commands should state the language standard, enable useful warnings, name the input files, and produce an output in a known location. Keeping these settings consistent prevents an example from accidentally depending on a compiler's default standard or an old executable left from a previous build. When a build fails, check the exact command and its exit status before trying to run the output.

Keep source files separate from generated artifacts when a project grows. Version control should normally track source, configuration, and documentation—not compiler object files or executables that can be recreated. The build system should describe how to recreate them.

Statements, expressions, and types

An expression computes a value, such as 2 + 3. A statement performs an action and commonly ends with a semicolon. A type describes which values an expression can represent and which operations make sense. The compiler uses these rules to find many mistakes before execution.

int total = 2 + 3;
std::cout << total << '\n';

Source code is not executed line by line by the compiler. Compilation happens first; then the operating system starts the executable, which begins at main. A process may read input, write output, and ask the operating system for resources while it runs.

Portable habits from the beginning

Check your understanding

  1. What is the difference between compiling a program and running it?
  2. Why can a compiler accept a file while the linker still reports an error?
  3. Find the compiler and version available to you, and check its documented C++23 support.
  4. Classify each problem as compile, link, runtime, or logic: a missing brace, an undefined function, a crash on a certain input, and a wrong total.
  5. Build a small program twice with the same command and confirm that the result is reproducible.

Key idea: C++ source is translated through a toolchain into a program. Knowing the stages makes errors easier to locate and helps you write portable code.

From the beginner's model to the C++ abstract machine

The compiler-and-executable picture is useful, but the language standard describes programs at a more abstract level. It specifies observable behavior: input and output, access to volatile objects, and other externally visible effects. An implementation may transform the program in any way that preserves the behavior required by the standard. This is the as-if rule: the implementation may act as if it had followed the source literally, even if its machine instructions look very different.

This explains why optimization is not simply a mechanical translation of each line. If the program has undefined behavior, the standard imposes no requirements for that execution, and the optimizer may make assumptions that invalidate a programmer's imagined behavior. A learning toolchain should therefore include warnings, tests, and—where available—sanitizers, in addition to a compiler.

Translation phases and reproducibility

For deeper diagnosis, learn to distinguish preprocessing tokens, language tokens, declarations, definitions, translation units, and linkage. The standard describes translation in phases, but compiler implementations commonly combine work internally. A preprocessed source listing can help reveal macro expansion; compiler output can reveal whether a source file was compiled with the expected options; linker maps and symbol tools can help investigate object-file boundaries.

A build is a function of more than source text. Compiler version, target architecture, standard-library implementation, macros, include paths, optimization flags, environment, and dependency versions can all affect the result. Record the essential configuration for experiments, especially when comparing results or reporting a compiler issue. Reproducibility is part of technical correctness: a result that cannot be rebuilt or its assumptions explained is difficult to verify.

Standards literacy and experimental method

When behavior is surprising, first write the smallest complete program that demonstrates it. State the language version and toolchain, compile with warnings, and test the behavior on a second implementation when portability is relevant. Then consult a reliable reference and identify whether the rule is guaranteed, unspecified, implementation-defined, or undefined. An experiment shows what one build did; it does not, by itself, establish what every conforming implementation must do.

Advanced C++ study benefits from separating three questions: what does the standard require, what does a particular implementation do, and what machine code does the selected build produce? These questions are related but not interchangeable. Compiler explorers, disassembly, and benchmarks answer implementation questions; standard wording and defect reports answer language questions.

Advanced investigation

  1. Compile one program with two optimization levels. Compare behavior and generated assembly, then explain which observations are guaranteed by C++ and which are implementation details.
  2. Inspect a preprocessed file and identify what an include guard or macro changed.
  3. Research one C++ defect report and summarize the original wording, the correction, and which published standards or implementations incorporate it.
  4. Write a short experimental report containing a hypothesis, source, compiler version, flags, observations, and a standards-based conclusion.
0% read