Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 1

Command Line C++: Compile & Run

Recommended resources

Hello! Welcome to your first lesson in the "High-Performance Native Telemetry Engine" course.

Your goal is to build a sophisticated C++ profiling tool, which requires a deep understanding of how C++ works "under the hood." Before we can dive into advanced topics like lock-free data structures and high-performance rendering, we need to establish a solid foundation. This first lesson is the most crucial step: learning how to take C++ source codeโ€”the text you writeโ€”and turn it into a program that the computer can actually execute.

Today, you will learn to write a basic C++ application, compile it, and run it directly from the command line. This skill is fundamental for any systems programmer and will give you direct control over the build process, which is essential for performance tuning later on.

The Journey from Source Code to Executable

Unlike interpreted languages, C++ code goes through a multi-stage process before it can be run. This process, called the build process, generally consists of three key stages: preprocessing, compilation, and linking. Understanding this pipeline is vital for diagnosing errors and optimizing your code.

C++ Compilation Process Explained
This diagram illustrates the transformation of C++ source files (`.cpp`, `.hpp`) into a final executable program through the stages of preprocessing, compilation, and linking.

To get a clear overview of this entire workflow, let's start with a video.

How C++ Works

The video 'How C++ Works' from The Cherno's excellent C++ series provides a practical and clear explanation of the C++ build process. It will walk you through creating a simple program and demonstrate what the compiler and linker do.

Watch the video from the beginning until 12:13. Pay close attention to the distinction between compiling a single file into an 'object file' and linking multiple object files into a final 'executable'.

To summarize and structure the key concepts from the video:

  1. Preprocessing: The first step, handled by the preprocessor. It scans your source code for special instructions called preprocessor directives, which begin with a #. The most common one is #include, which literally copies and pastes the content of another file (usually a header file) into your source file. It also expands macros (#define).
  2. Compilation: The compiler takes the preprocessed code and translates it into low-level machine code, also known as object code. Each .cpp file is compiled independently into a corresponding object file (e.g., main.o or main.obj). This is the stage where the compiler checks for syntax errors. If you forget a semicolon, the compiler will report an error and stop.
  3. Linking: The linker's job is to take all the necessary object files, including those from your project and those from standard libraries (like the one for iostream), and "link" them together into a single, final executable file (e.g., my_app.exe on Windows or my_app on Linux/macOS).

For a slightly more granular breakdown of these steps, including the intermediate files generated, you can review the following article.

C++ Compilation process

The GeeksForGeeks article 'C++ Compilation process' provides a concise, step-by-step summary of the build pipeline, including the command-line flags used to stop at each stage.

Read through the sections 'Steps involved in Compilation', 'Tools Involved in the Compilation Pipeline', and 'Common Errors at Each Stage'. This will reinforce the concepts from the video and provide a handy reference table.


Setting Up Your Compiler

To compile C++ code from the command line, you need a C++ compiler. We will use g++, which is part of the GNU Compiler Collection (GCC). It's a powerful, widely-used compiler that is standard in the open-source world and for performance-oriented development.

The following guide provides instructions for installing g++ on Windows, macOS, or Linux.

C++ With the Command Line

The article 'C++ With the Command Line' from the USACO Guide is a practical resource for getting your environment set up. It provides clear, OS-specific instructions for installing the g++ compiler.

Follow the instructions in the 'Installing g++' section that correspond to your operating system (Linux, Mac, or Windows). For Windows, the 'Simpler: Mingw-w64' option is recommended. Once you're done, you should be able to open a terminal or command prompt and get a successful response from running g++ --version.


Your First C++ Program: "Hello, World!"

Now that you have a compiler, let's write, compile, and run your first program.

1. Write the Code

Create a new file named main.cpp using a plain text editor (like VS Code, Sublime Text, or even Notepad) and type the following code:

#include <iostream>

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

Let's quickly break this down:

  • #include <iostream>: A preprocessor directive that includes the standard library for input and output operations (like printing to the console).
  • int main(): The main function. Program execution begins here. Every C++ application must have a main function.
  • std::cout << "Hello, World!" << std::endl;: This line does the printing.
    • std::cout is the standard character output stream.
    • The << operator is used to "send" data to the stream.
    • std::endl inserts a newline character and flushes the stream.
  • return 0;: The main function returns an integer status code. A return value of 0 signifies that the program executed successfully.

2. Compile and Run

Now, open your terminal or command prompt, navigate to the directory where you saved main.cpp, and execute the following command:

g++ main.cpp -o hello

Let's dissect this command:

  • g++: The program you're running (the compiler).
  • main.cpp: The source file you want to compile.
  • -o hello: A compiler flag. -o stands for "output," and hello is the name you want to give the resulting executable file.

If there are no errors, this command will produce a new file in the same directory. On Linux/macOS, it will be named hello. On Windows, it will be hello.exe.

To run your program, type the following in your terminal:

  • On Linux/macOS: ./hello
  • On Windows: hello or .\hello.exe

You should see Hello, World! printed to your console. Congratulations, you've just built and run your first C++ application!

The "C++ With the Command Line" guide you looked at earlier also walks through this exact process. You can refer to the "Basics of Compiling & Running" section if you need to double-check the commands.


Multi-File Projects and Linker Errors

Real-world applications, including the profiler you'll be building, are split across many files for organization and modularity. This is where the linker's job becomes critical.

Let's see what happens when we use two files.

  1. Create a new file, log.cpp, and add the following function definition:
    #include <iostream>
    
    void PrintMessage() {
        std::cout << "This message is from another file!" << std::endl;
    }
    
  2. Modify your main.cpp to call this function. Notice that we need to add a declaration for PrintMessage() before main so the compiler knows it exists.
    #include <iostream>
    
    // Declaration of the function
    void PrintMessage();
    
    int main() {
        std::cout << "Calling a function from main..." << std::endl;
        PrintMessage();
        return 0;
    }
    

To compile this multi-file project, you provide all source files to the compiler:

g++ main.cpp log.cpp -o multi_file_app

The compiler first creates main.o and log.o behind the scenes. The linker then sees that main.o calls PrintMessage, finds its definition in log.o, and connects them to produce the final multi_file_app executable.

The following video segment demonstrates this concept visually and also shows what happens when the linker cannot find a function definition, resulting in a common and often confusing linker error.

How C++ Works

Let's return to the 'How C++ Works' video. This next segment is crucial as it directly explains how to manage a multi-file project and how to interpret the infamous 'unresolved external symbol' or 'undefined reference' linker error.

Watch the video from 12:13 to 19:44. Focus on the difference between a function 'declaration' (the promise) and a 'definition' (the implementation), and see how the linker's job is to connect the two across different files.

Test your understanding!

You have two files, main.cpp and math.cpp.

math.cpp:

int multiply(int a, int b) {
    return a * b;
}

main.cpp:

#include <iostream>

// Declaration is missing!

int main() {
    std::cout << "2 * 3 = " << multiply(2, 3) << std::endl;
    return 0;
}
  1. If you try to compile with g++ main.cpp math.cpp -o app, will you get a compiler error or a linker error? Why?
  2. How would you fix the code in main.cpp?
Show answer
  1. You will get a compiler error. The compiler processes main.cpp and sees a call to a function named multiply that it has never seen before (because there is no declaration). It will fail with an error like "multiply was not declared in this scope." The process won't even get to the linking stage.

  2. You would fix it by adding a function declaration (also called a prototype) for multiply in main.cpp before the main function:

    #include <iostream>
    
    // Add this declaration
    int multiply(int a, int b);
    
    int main() {
        std::cout << "2 * 3 = " << multiply(2, 3) << std::endl;
        return 0;
    }
    

    With this fix, main.cpp compiles successfully. Then, during the linking stage, the linker finds the definition of multiply in the object file generated from math.cpp and successfully creates the executable.


Essential Compiler Flags

You can control the compiler's behavior with flags. For this project, you should always use the following set of flags:

g++ -std=c++17 -O2 -Wall main.cpp -o app
  • -std=c++17: This tells the compiler to use the C++17 standard. This enables modern language features we will use throughout the course.
  • -O2: This enables a high level of code optimization. The compiler will work harder to make your code run faster. This is directly relevant to your goal of building a high-performance application.
  • -Wall: This enables "all" warnings. The compiler will warn you about code that is syntactically correct but potentially problematic or error-prone. Always pay attention to warnings and fix them.

Summary and Next Steps

In this lesson, you've taken the first essential step into C++ systems programming.

Key Takeaways:

  • The C++ build process involves preprocessing, compilation, and linking.
  • The compiler turns a single .cpp source file into an object file (.o) and checks for syntax errors.
  • The linker combines multiple object files and libraries into a single executable file and resolves references between them.
  • You can compile and run a C++ program from the command line using g++, for example: g++ main.cpp -o my_app.
  • For multi-file projects, you must provide all source files to the compiler: g++ main.cpp utils.cpp -o my_app.
  • Useful compiler flags like -std=c++17, -O2, and -Wall help you write modern, fast, and safe code.

In the next lesson, "Differentiating between fundamental data types, and declare/initialize variables," we will move from the build process to the code itself. You'll learn about the basic data types (integers, floats, characters) that form the building blocks of any program and are the foundation for storing the profiling data you will eventually capture.