Writing Your First Program
Introduction & Setup

0.2 Writing Your First Program

Let's write your first C++ program. I will start with the traditional 'Hello, World!' example. We will compile it, run it, and then break down exactly what every single character is doing. Unlike newer languages that hide the boilerplate (looking at you, Python), C++ requires you to understand the structural layout of your source code file. There's no magical runtime hiding under the hood.

Here is the program:

#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}
Your first C++ program: hello_world.cpp

Let's walk through this line-by-line:

1. The Preprocessor and `#include`

The first line starts with a hash symbol: #include <iostream>. In C++, lines starting with # are processed by the preprocessor before the compiler even glances at your code. Think of the preprocessor as a glorified, super-fast copy-paste tool. It looks for the file named <iostream> in the system library directories and copy-pastes its entire contents directly to the top of your file. This header contains declarations that allow us to talk to the terminal.

Because the preprocessor literally copy-pastes code, including <iostream> expands your tiny 5-line program to over 30,000 lines of declarations before the compiler even starts parsing it! You can inspect this preprocessed output yourself by running g++ -E hello_world.cpp in your terminal.

2. The `main` Function

In C++, every application must have a function called main. This is the entry point where the operating system starts executing your program. If you forget to write main, the linker will throw a massive fit and refuse to build. The syntax is:

int main() {
    // your instructions here
}
The entry point syntax in C++.

Notice the int prefix. This tells the compiler that the main function must return an integer when it completes. By convention, returning 0 means the program ran successfully. Returning any other number (like 1 or -1) is C++'s way of telling the OS, 'Hey, something went terribly wrong.'

3. Writing to the Console with `std::cout`

Line 4 is where the actual work happens: std::cout << "Hello, World!" << std::endl;.

std::cout stands for standard character output*. It represents your terminal screen.

* The << symbol is the insertion operator. Think of it as a slide pushing the string "Hello, World!" into the console stream.

std::endl stands for end line*. It does two things: it moves the cursor to the next line, and it flushes the output buffer (forces the computer to display it immediately on screen).

* Namespace Prefix (std::): C++ organizes its library functions inside namespaces to prevent name collisions. The standard library is housed in the std namespace (short for 'standard'). Thus, we write std::cout to indicate it comes from the standard library.

Under the Hood: Assembly Output

To show you what the CPU actually executes, I've compiled this program to x86_64 assembly instructions. When we write std::cout << ..., the compiler translates it into loading arguments into CPU registers and calling standard library write routines:

main:
    push    rbp              ; Establish stack frame pointer
    mov     rbp, rsp
    ; Load address of string and std::cout stream pointer
    mov     rsi, OFFSET FLAT:.LC0  ; Address of "Hello, World!"
    mov     rdi, OFFSET FLAT:std::cout
    call    std::operator<<    ; Invoke stream insertion operator
    mov     rsi, OFFSET FLAT:std::endl ; Load address of std::endl
    mov     rdi, rax         ; Move output stream stream handle
    call    std::ostream::operator<< ; Invoke endl manipulator
    mov     eax, 0           ; Set exit return code to 0
    pop     rbp              ; Restore stack frame pointer
    ret                      ; Return control to the OS
Simplified x86_64 Assembly generated for main.

4. The Semicolon (The Compiler's Favorite Traps)

Notice the semicolon ; at the end of lines 4 and 5. In C++, statements do not end with newlines; they must end with a semicolon. Forgetting a semicolon is a rite of passage. If you miss one, the compiler will act like the world has ended and print 50 lines of unrelated errors.

The Compilation Model

When you run a Python script, an interpreter reads and runs it line-by-line. C++ doesn't work that way. We must compile our code into a raw machine readable binary file first.

Here is the pipeline that happens behind the scenes:

1. Preprocessing: The preprocessor handles #include files and macro expansion, creating a single temporary code bundle.

2. Compilation: The compiler translates the preprocessor output into assembly code, translating high level concepts into instructions specific to your CPU architecture.

3. Assembly: An assembler converts the assembly code into machine code binary files called object files (often ending in .o or .obj).

4. Linking: The linker takes your object files and glues them together with compiled standard library files into a single, executable file (e.g. a.out on Linux/macOS, hello.exe on Windows).

Let's see this visually:

The C++ compilation workflow: Preprocessor, Compiler, Assembler, and Linker.

If you are using a command line compiler like GCC or Clang, we can compile and run this file by typing:

# Compile the code
g++ -std=c++20 hello_world.cpp -o hello

# Run the executable
./hello
Compiling and running hello_world.cpp using g++ in terminal.

The -std=c++20 flag tells the compiler to compile using the C++20 standard, ensuring we have access to all modern features.

Compiler Optimization Levels (O0, O2, O3)

When compiling C++, you choose the level of optimization. These are set using compiler flags in your terminal command:

* -O0 (No Optimization): The default. The compiler translates your code 1-to-1 directly into machine instructions. It is slow, but it makes debugging and step by step execution extremely easy.

* -O2 (Production Standard): The compiler analyzes your code and restructures it, performing optimizations like function inlining and loop adjustments. It is the industry standard for production binaries.

* -O3 (Aggressive Optimization): The compiler uses aggressive vectorization and CPU specific instructions. It generates the fastest possible code, but it can increase binary sizes and lead to unexpected behavior in complex multithreaded systems.

Troubleshooting Common Compiler Errors

If you get build errors, inspect these common culprits:

* Linker Error (undefined reference to 'main'): You either misspelled main (e.g. Main or mian) or forgot to define it altogether.

* Preprocessor Error (iostream: No such file or directory): You forgot the angle brackets or misspelled <iostream>.

Finished reading this lesson?