C++
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented C++
-
Section 4: Memory Management
-
Section 5: Templates and Generic Programming
-
Section 6: The Standard Template Library
-
Section 7: Modern C++ Features
-
Section 8: Error Handling
-
Section 9: Multithreading
-
Section 10: Operator Overloading and Type Conversion
-
Section 11: Advanced Topics
-
Section 12: Tooling and Build Systems
-
Section 13: Design Patterns in C++
-
Section 14: Interfacing with C and Systems Programming
-
Section 15: Networking and IPC Basics
-
Section 16: Graphics and Game Programming Basics
-
Section 17: The Boost Libraries
-
Section 18: Data Structures and Algorithms in C++
-
Section 19: Practical Projects
-
Section 20: More Concurrency Patterns
-
Section 21: More OOP and Design Practice
-
Section 22: File I/O and Streams
-
Section 23: More Standard Library
-
Section 24: Practice Exercises
-
Section 25: Interview and Algorithm Practice
-
Section 26: Compiler and Language Internals
-
Section 27: GUI and Application Frameworks Overview
-
Section 28: Testing and Quality Practices
-
Section 29: Numerics Library
-
Section 30: Concepts Library (C++20)
-
Section 31: Ranges Library (C++20) In Depth
-
Section 32: More Utility Library
4: Compilation Stages: Preprocessing, Compiling, Linking
I once spent an entire Friday afternoon staring at a single line of code that refused to compile. The error message was a cryptic "undefined reference to GameEngine::initialize()," but the function was right there—I could see it in the .cpp file, the signature matched the header perfectly, and I had saved all my files. I felt like I was losing my mind because, logically, the code was correct. The problem wasn't my logic; it was that I hadn't actually told the compiler to include that specific source file in the build process. The compiler was happy, but the linker was screaming because it had a "promise" of a function (the declaration) but no "delivery" (the definition).
To avoid that kind of frustration, you have to stop thinking of "compiling" as a single button click. In C++, what we call compiling is actually a pipeline of three distinct stages. If you don't know where a bug is occurring in that pipeline, you're just guessing.
The Preprocessor's Hidden Text-Editing
Before the actual compiler ever sees your code, the preprocessor runs. Think of it as a sophisticated "find and replace" tool. It doesn't understand C++ syntax; it only understands directives that start with a hash symbol (#). When you write #include <iostream>, the preprocessor literally copies the entire content of that header file and pastes it right there into your source code. It's a massive copy-paste job.
This is also where macros live. If you define #define MAX_BUFFER 1024, the preprocessor swaps every instance of MAX_BUFFER for 1024 before the compiler starts. This is why macro errors are so annoying—the compiler reports an error on a line that doesn't actually exist in your source file, but exists in the "expanded" version the preprocessor created. I generally recommend avoiding macros for constants (use constexpr instead), but you'll see them in older codebases everywhere.
Turning Source into Object Code
Now that the preprocessor has finished its text manipulation, the actual compiler takes over. It looks at a single "translation unit" (usually one .cpp file and all its included headers) and translates that C++ code into machine code. However, it doesn't create a finished program yet. Instead, it produces an object file (usually ending in .o or .obj).
Here is the crucial part: the compiler handles each file in isolation. If your Player.cpp calls a function defined in Physics.cpp, the compiler doesn't go looking for that function. It simply leaves a "placeholder" or a note in the object file saying, "I don't know where calculateGravity() is yet, but someone better provide it later." This is why you can change one file in a massive project and only have to re-compile that one file, rather than the whole system.
The Final Puzzle Piece: Resolving Symbols
The linker is the final stage. Its job is to take all those separate object files and stitch them together into a single executable. It goes through those placeholders the compiler left behind and matches them with the actual memory addresses where those functions live.
When you see an "undefined reference" or "unresolved external symbol" error, that is the linker talking. It's saying, "You told me there would be a function called GameEngine::initialize(), I've looked through every object file you gave me, and it's nowhere to be found." This happens if you forget to add a .cpp file to your build command or if you declared a function in a header but forgot to actually write the body of it in the source file. I've spent more time fighting the linker than I care to admit, mostly because the errors feel like they're coming from a ghost in the machine.
📋 Practical Task
Exercise: Investigating the Translation Pipeline
In this exercise, you will manually trigger each stage of the compilation process to see exactly what the computer is doing behind the scenes. You will need a terminal and a compiler like GCC or Clang.
- Create two files:
main.cpp: Include a simple header#include "constants.h"and amainfunction that prints a value from that header.constants.h: Define a macro#define VERSION 1.
- Inspect the Preprocessor: Run your compiler with the
-Eflag (e.g.,g++ -E main.cpp -o main.i). Openmain.i. Find where your#includewas; you'll see hundreds of lines of code from the system headers pasted into your file. - Create an Object File: Run the compiler with the
-cflag (e.g.,g++ -c main.cpp -o main.o). Try to openmain.oin a text editor. You'll see it's binary (gibberish), confirming that the source code has been translated to machine code. - Force a Linker Error:
- Add a function declaration to
main.cpp:void ghostFunction(); - Call
ghostFunction();inside yourmain. - Try to compile the whole thing normally (
g++ main.cpp -o program).
- Add a function declaration to
- Analyze: Observe that the compiler doesn't complain during the first phase (because the declaration promised the function exists), but the linker fails at the very end. Note the specific wording of the error message.
There are no comments for now.