# Welcome!

C++ can feel overwhelming!

Maybe you first encountered it in CS2040C algorithms & data structures module. Maybe you heard horror stories about pointers and seemingly mysterious segmentation faults that span 300 lines!

Despite its pitfalls, C++ remains the language of choice behind the many of the world's fastest systems. This includes high performance databases like [Clickhouse](https://clickhouse.com/) and [RocksDB](https://rocksdb.org/), game engines like [Unreal Engine](https://www.unrealengine.com/en-US/unreal-engine-5) and browsers like [Google Chrome](https://www.google.com/intl/en_sg/chrome/).&#x20;

### Why C++?

Unlike purely managed languages, C++ does not shy away from the messy details. Instead, it forces one to understand what your program is actually doing. This includes understanding how memory layout, object lifetimes and resource management actually works. While this might appear initially uncomfortable, it helps builds deep intuition about how computers actually work.

### Using this guide

If you are new to C++, you are highly encouraged to read through the content in order. The guide walks you through some of C++ fundmwnrtasl, common memory pitfalls and how modern C++ features are designed to help you tackle and avoid them.

If however, you are familiar with C++ constructs (i.e variables, loops, functions), feel free to skip ahead towards the [Memory & Ownership](/memory-and-ownership) section. This introductory workshop is tailored towards begineers so you might find the content a bit brief.

### Who Am I?

I'm Benn Tan, a computer science undergraduate from the National University of Singapore and a core member of NUS Hackers.

I've previously used C++ in my own free time and I hope to share some of the knowledge gained through [learncpp.benntan.com](https://learncpp.benntan.com)


# C++ Fundamentals

This guide aims to provide you with the fundamentals of C++. It's **not** meant to be an exhaustive guide. For more in-depth details, you should refer to the [official C++ reference](https://en.cppreference.com/w/).

We do assume that you have basic programming knowledge and are cognizant of common programming constructs (i.e variables, functions, loops).

### Getting started

To run C++ code, you will need a **compiler**. Alternatively, you can also use an online compiler like [JDoodle](https://www.jdoodle.com/online-compiler-c++17) to follow along.&#x20;

#### Mac

To check if you have the `clang++` compiler installed, run:

```bash
clang++ --version
```

If not, you can install it via:

```bash
xcode-select --install
```

A software update window will pop up. You will need to agree to the licensing agreement before you can commence installing.

If all goes well, you should be greeted with:

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2F9UIqViQErJVyEM2gw4u3%2FScreenshot%202026-04-01%20at%201.36.05%E2%80%AFAM.png?alt=media&amp;token=30be7105-61fe-472d-91ce-1f6d9b7963a4" alt=""><figcaption></figcaption></figure>

#### Windows

On Windows, the setup process varies. The simplest option is to install an Integrated Development Environment (IDE) like [Dev-C++](https://www.dev-cpp.com/) that bundles with a C++ compiler.&#x20;

#### Ubuntu / Debian

To check if you have the `g++`  compiler installed, you can check via:

```bash
g++ --version
```

If it is not installed, you can install it via your favourite package manager.

```bash
sudo apt update
sudo apt update g++
```


# Program Execution Model

When one develops a C++ program, it does not run directly. Instead, it goes through a fixed sequence of steps before your computer actually understands and executes it.

### Running Your Source Code

Let's start with a simple single-file C++ program. The following *prints **Hello World*** to the console.

{% code title="main.cpp" %}

```cpp
#include <iostream>

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

{% endcode %}

This file is called the **source code.**

To run it, we need to compile the code:

{% code title="" %}

```sh
clang++ main.cpp -o hello-world
```

{% endcode %}

This will produce an executable file called `hello-world` which your computer can finally run:

{% code title="" %}

```sh
./hello-world
```

{% endcode %}

Each time you make changes to the source code, you will need to compile again.

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FtufIzMGwYQZhDVJYVXJe%2FScreenshot%202026-04-01%20at%202.03.33%E2%80%AFAM.png?alt=media&amp;token=0b9cc0bb-11aa-4a72-9ead-b187ca062725" alt=""><figcaption></figcaption></figure>

### What Just Happened ?!

Think of a compiler as a black box that simply ***converts*** source code to machine code.

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FLXNID9aisD16b8c2lK6Z%2Fimage.png?alt=media&amp;token=d252db31-b1fe-4e5b-8763-3c6c01f22680" alt=""><figcaption><p>src: <a href="https://www.sitesbay.com/cpp/cpp-compiler">https://www.sitesbay.com/cpp/cpp-compiler</a></p></figcaption></figure>

<details>

<summary>What does a compiler exactly do?</summary>

This is beyond the scope of this workshop.

If you happen to be curious:

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FiYmRmQ42CtdcctO0RF8e%2Fimage.png?alt=media&amp;token=e77a2b96-5be7-44b7-bd48-775239f620f6" alt=""><figcaption><p>src: <a href="https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html">https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html</a></p></figcaption></figure>

Compiling a C++ source code is usually a 4 step process. It composes of&#x20;

**a) Preprocessing** where we substitute macros and header files with their actual content

**b) Compilation** from C++ to platform-specific assembly

**c) Assemble** where the resultant assembly code is assembled into actual object code

**d) Linking** to link external library functions that the executable needs

You can even pause the compilation process at each stage to inspect the immediate outputs using varying CLI flags.

</details>


# Basic Syntax & I/O

Let's kick things off by familiarising ourselves with the basic structure of a C++ program.

### Anatomy of a C++ Program

Recall the source code you previously saw:

{% code title="main.cpp" %}

```cpp
#include <iostream>

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

{% endcode %}

Breaking it down:

<table><thead><tr><th width="190.94439697265625">Code</th><th>What it does</th></tr></thead><tbody><tr><td><pre><code>#include &#x3C;iostream>
</code></pre></td><td>Brings in the standard I/O library so that we can use things like <code>std::cout</code></td></tr><tr><td><pre><code>int main() {
</code></pre></td><td><p>The entry point of every C++ program.</p><p></p><ul><li><code>int</code>  indicates the return type of the function </li><li><code>main</code> is the function name.</li></ul></td></tr><tr><td><pre><code>std::cout &#x3C;&#x3C; "Hel...
</code></pre></td><td><p>Prints the text to the console.</p><p></p><p>👉 You can remember <code>&#x3C;&#x3C;</code> as <em>pushing</em> the text into the output stream (i.e <code>std::cout</code> in this case)</p></td></tr><tr><td><pre><code>return 0;
</code></pre></td><td><p>Recall that <code>int</code> is the return type of the <code>main</code> function.</p><p></p><p>Hence, at the end of our <code>main</code> function, we should<sup>[1]</sup> return an int.</p><p></p><p>You can return anything, really. But usually returning 0 indicates our program has successfully executed.</p></td></tr></tbody></table>

{% hint style="info" %}
Try augmenting the `Hello, world!` string to something else like your name!

![](https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FSClCOUIZ3CiAhW9OdMhO%2FScreenshot%202026-04-01%20at%202.36.09%E2%80%AFAM.png?alt=media\&token=abae9342-c405-48a6-b30c-862215d536be)
{% endhint %}

<sub>\[1] Your code will still compile even if you don't return anything from the main function.  This is because C++ implicilty returns 0 at the end of</sub> <sub></sub><sub>`main()`</sub><sub>. For more info, refer</sub> [<sub>here</sub>](https://stackoverflow.com/questions/19293642/why-does-the-main-function-work-with-no-return-value)<sub>.</sub>

### Newlines

If you try printing multiple times, you will notice that the output gets concatenated on the same line. You can fix this by adding a newline using `std::endl` .

<table><thead><tr><th>Code</th><th>Output</th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">#include &#x3C;iostream>

int main()
{
std::cout << "benn";
std::cout << "tan";
std::cout << "jia";
return 0;
} </code></pre></td><td><p><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FfJoApN4ZjyCIi3j7Er6b%2FScreenshot%202026-04-01%20at%202.42.36%E2%80%AFAM.png?alt=media&amp;token=3e77902b-17a4-4b57-8ca7-2ade0c010675" alt="" data-size="original"></p><p></p></td></tr><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">#include \<iostream>

int main()
{
std::cout << "benn" << std::endl;
std::cout << "tan" << std::endl;
std::cout << "jia";
return 0;
} </code></pre></td><td><p><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2F0YuRCZsSWvEK5EW1mQNn%2FScreenshot%202026-04-01%20at%202.43.39%E2%80%AFAM.png?alt=media&amp;token=78fef5f8-bc0e-4466-b689-f6d7321591d2" alt="" data-size="original"></p><p></p></td></tr></tbody></table>

### Input

It's not quite fun if we hardcode everything into program. Let's augment our program to accept user input externally.

To read input from the user, we can use `std::cin >>`

{% code title="main.cpp" %}

```cpp
#include <iostream>

int main() {
    int x;
    std::cin >> x;
    std::cout << "You entered: " << x << std::endl;
}
```

{% endcode %}

{% hint style="info" %}
Note that the direction of the arrows (`>>`) for `std::cin` is now reversed compared to `<<`.&#x20;

👉 You can remember as **data flowing into `x`**.
{% endhint %}

### Namespaces

If you find prefixing `std::` before the function calls cumbersome, you can  omit it with `using namespace std`

{% code title="main.cpp" %}

```cpp
#include <iostream>
using namespace std;
int main() {
    int x;
    cin >> x;
    cout << "You entered: " << x << endl;
}
```

{% endcode %}

However, this is generally discouraged in larger projects as it does lead to naming conflicts.

👉 It's usually best practice to be explicit and use `std::`&#x20;

### Exercise

Build a program that takes in and prints the result of the summation of two numbers.


# Variables

Now that we are able to accept input and print output externally, we need a mechanism to **store data** inside our program.

### What is a Variable?

A variable is simply an named container that stores some value.

{% code title="main.cpp" %}

```cpp
int x = 5;
```

{% endcode %}

In the example above,

* `int`  indicates the **type** of the variable
* `x`  is the **name** of the variable
* `5` is the **value** of the variable

### Common Data Types

|               |                             |                         |
| ------------- | --------------------------- | ----------------------- |
| `int`         | `int age = 24`              | Integer (whole numbers) |
| `double`      | `double pi = 3.14`          | Floating point numbers  |
| `char`        | `char x = 'c'`              | Singular character      |
| `bool`        | `bool isGood = true`        | True / False            |
| `std::string` | `std::string name = "Benn"` | Sequence of characters  |

{% hint style="info" %}
⚠️ Common Pitfall

Use double quotes (`" "`) for strings and single quotes `' '` for characters
{% endhint %}

### Declaration vs Initialisation

Declaration is when we define a variable's **type** and **name** without assigning it a **value** yet. Initialisation is when we give it a **value**.

{% code title="main.cpp" %}

```cpp
int x;      // declaration
x = 10;     // assignment

int y = 10; // declaration + initialisation
```

{% endcode %}

### Arithmetic Operators

You can also perform arithmetic operations just like how you would do in normal math:

{% code title="main.cpp" %}

```cpp
int x = 10;
int y = 12;

int sum = x + y;
int difference = x - y;
int product = x * y;
double quotient = x / y; // note that we use the double here
int remainder = x % y;

int expression = ((x + y) * (x - y));
```

{% endcode %}

{% hint style="info" %}
⚠️ Common Pitfall

Integer division **truncates** the division&#x20;

So `10 / 12 = 0` instead of `0.83`&#x20;
{% endhint %}

### Exercise

Extending from our previous exercise, we now want to support different operations beyond addition. Our mini-calculator should now be able to add, subtract, multiply and divide two numbers!


# Control Flow

So far, our programs have executed line-by-line, step-by-step from top to bottom. In the real world however, we might want to

* make decisons based on a condition
* repeat certain actions
* organise logic into reusable pieces

This is where **control flow** comes in

### Conditionals

Conditionals let our program make decisions.

{% code title="main.cpp" %}

```cpp
int score = 85;

if (score == 100) {
    std::cout << "Perfect" << std::endl;
} else if (score >= 90) {
    std::cout << "Grade A" << std::endl;
} else if (score >= 80) {
    std::cout << "Grade B" << std::endl;
} else if (score >= 70) {
    std::cout << "Grade C" << std::endl;
} else {
    std::cout << "Needs improvement" << std::endl;
}
```

{% endcode %}

{% hint style="info" %}
⚠️ Common Pitfall

`=` means assignment while `==` checks for equality

{% code title="" %}

```cpp
int x = 5;      // assignment
x == 5;         // comparison
```

{% endcode %}
{% endhint %}

### Loops

Loops lets us repeat certain blocks of code

#### while loop

A `while` loop keeps running as long as the condition is fufilled.

<table><thead><tr><th width="354.14404296875">Code</th><th>Output</th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title=""><code class="lang-cpp">int x = 1;

while (x <= 5) {
std::cout << x << std::endl;
x++;
} </code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FoJ7CSGqdrlVowJ5NlIoJ%2FScreenshot%202026-04-01%20at%2012.19.51%E2%80%AFPM.png?alt=media&amp;token=61371562-b3f1-4111-8b81-b04c5ee43b63" alt="" data-size="original"></td></tr></tbody></table>

#### for loop

A `for` loop is useful when we know how many times we want to repeat something.

<table><thead><tr><th width="353.73614501953125"></th><th></th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">for (int i = 1; i &#x3C;= 5; i++) {
    std::cout &#x3C;&#x3C; i &#x3C;&#x3C; std::endl;
}
</code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FZPGybIJMyVDLdyPz8gwc%2Fimage.png?alt=media&amp;token=c58f635c-8366-4053-bb16-2ccad19d6f2e" alt="" data-size="original"></td></tr></tbody></table>

#### break / continue

`break` forces the loop to terminate prematurely.

`continue` skips the rest of the current iteration.

<table><thead><tr><th width="354.77777099609375"></th><th></th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">for (int i = 1; i &#x3C;= 5; i++) {
    if (i == 3) {
        break;
    }
    std::cout &#x3C;&#x3C; i &#x3C;&#x3C; std::endl;
}
</code></pre></td><td><p><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FzZku9rI5F14nT7hLXj8O%2FScreenshot%202026-04-01%20at%2012.25.16%E2%80%AFPM.png?alt=media&amp;token=d735921b-8cf2-4b1d-b0f4-d5cf080af9c4" alt="" data-size="original"></p><p></p></td></tr><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">for (int i = 1; i &#x3C;= 5; i++) {
    if (i == 3) {
        continue;
    }
    std::cout &#x3C;&#x3C; i &#x3C;&#x3C; std::endl;
}
</code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FgnPvKN5gA6unYabaI6LC%2FScreenshot%202026-04-01%20at%2012.24.30%E2%80%AFPM.png?alt=media&amp;token=8b996c70-12ba-47b3-9aae-c5f01bcf5f16" alt="" data-size="original"></td></tr></tbody></table>

### Functions

Functions let us group code into reusable blocks.

#### Basic Usage

{% code title="main.cpp" %}

```cpp
#include <iostream>

void greet() {
    std::cout << "Hello!" << std::endl;
}

int main() {
    greet();
}
```

{% endcode %}

Here:

* `void` means the function does not return anything
* `greet` is the function name
* `greet()`  *calls* the function, causing **Hello!** to be printed.

#### Function Parameters

Our current `greet` function always print the same message (which isn't very flexible). Let's augment it by adding a parameter so that our greeting message can change depending on the person.

{% code title="main.cpp" %}

```cpp
#include <iostream>

void greet(std::string name) {
    std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
    greet("Benn");
    greet("Bryan");
    greet("Anton Tim");
}
```

{% endcode %}

#### Functions Return Value

A return value is the value the function sends back to the caller after it's done executing. At the call site, you can use the result however you wish.

{% code title="main.cpp" %}

```cpp
#include <iostream>

int add(int x, int y) {
    return x + y;
}

int main() {
    int result = add(3, 4);
    std::cout << result << std::endl;
}
```

{% endcode %}

### Exercise(s)

⭐ Write a function `countdown(int n)` that takes in a number `n` and prints from `n` down to 1.

{% code title="" %}

```shellscript
countdown(5)
5
4
3
2
1
```

{% endcode %}

⭐⭐ Write a program that hardcodes a secret number. It will repeatedly ask the user to guess. Depending on the user's guess, the program will feedback **Too low, Too high** or **Correct.**

{% code title="" %}

```
Guess the number: 50
Too high

Guess the number: 25
Too low

Guess the number: 30
Too low

Guess the number: 42
Correct!
```

{% endcode %}


# Memory & Ownership

So far, we've been using variables without really thinking **where they live** / **who owns them.**

In C++, understanding memory is extremely important as it helps you write

* faster programs
* safer code
* avoid nasty bugs like memory leaks


# Pointers

### Address of Variables

So far, we've been working with variables like:

{% code title="main.cpp" %}

```cpp
int x = 10;
```

{% endcode %}

But sometimes, we don't want the value itself, rather we will want to know **where is this value stored in memory?**

The variable `x` could have some address like `0x100..`.&#x20;

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FnZUGBzKxpys7S4bzQL67%2FScreenshot%202026-04-04%20at%2011.44.10%E2%80%AFPM.png?alt=media&amp;token=e43635fb-2d32-4c3d-ac50-9009c887e1f0" alt=""><figcaption></figcaption></figure>

We don't know so lets find out using the ampersand `&` operator.

{% code title="main.cpp" %}

```cpp
int main()
{
    int x = 10;
    std::cout << "location of x: " << &x << std::endl;
}
```

{% endcode %}

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FPD2qi9T4kRKF35lEEC7c%2FScreenshot%202026-04-04%20at%2011.46.24%E2%80%AFPM.png?alt=media&amp;token=f68eb140-468a-4134-88f4-cd60255a920f" alt=""><figcaption></figcaption></figure>

### Type of Pointers

We know the type of `x`  is an `integer` , but what's the type of `&x` ?&#x20;

Instead of second guessing ourselves, let's rely on the C++ compiler to tell us. C++ offers a [`typeid` function ](https://en.cppreference.com/w/cpp/language/typeid.html)that seems useful:

{% code title="main.cpp" %}

```cpp
int main()
{
    int x = 10;
    std::cout << typeid(x).name() << std::endl;
    std::cout << typeid(&x).name() << std::endl;
}
```

{% endcode %}

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FIEwBlHA5qY6F1meUD4La%2FScreenshot%202026-04-04%20at%2011.52.29%E2%80%AFPM.png?alt=media&amp;token=a7398563-ced5-497a-afcf-79af3eaf7fd3" alt=""><figcaption></figcaption></figure>

The output may look cryptic but:

* `i` (as you might have guessed) represents **int**
* `Pi` (which you also might have guessed) represents **pointer to int** (`int*`)

Indeed, we can write it like so:

<table><thead><tr><th>Code</th><th>Visual Aid</th></tr></thead><tbody><tr><td><pre class="language-cpp"><code class="lang-cpp">int main()
{
    int x = 10;
    int* p = &#x26;x;
}
</code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FoFLv9G7XuoFfKTdnRKR4%2FScreenshot%202026-04-04%20at%2011.59.56%E2%80%AFPM.png?alt=media&amp;token=bf80117e-105f-4d32-8fe8-75237f4f6aaf" alt=""></td></tr></tbody></table>

### De-referencing

Given a pointer of type (`int *`),  is there a way I can get the *actual* *value (*&#x61;nd not the address) it points to?&#x20;

In other words,

{% code title="main.cpp" %}

```cpp
int main()
{
    int x = 10;
    int* p = &x;
    
    // can i get 10 using variable p alone?
}
```

{% endcode %}

Yes we can! Since the pointer `p`  stores an address, we can dereference the pointer using `*` to get the value! Think of dereferencing as follow the address to get the value stored there.

<table><thead><tr><th>Code</th><th>Visual Aid</th></tr></thead><tbody><tr><td><pre class="language-cpp"><code class="lang-cpp">int main()
{
    int x = 10;
    int* p = &#x26;x;
    int y = *p;

```
std::cout &#x3C;&#x3C; "value of x: " &#x3C;&#x3C; x &#x3C;&#x3C; std::endl;
std::cout &#x3C;&#x3C; "value of y: " &#x3C;&#x3C; y &#x3C;&#x3C; std::endl;
```

} </code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FBY2RLYIfZUkWUa2CJWk3%2FScreenshot%202026-04-05%20at%2012.15.36%E2%80%AFAM.png?alt=media&amp;token=fa6b774c-f972-4fe9-9842-a810aa00afa5" alt=""></td></tr></tbody></table>

Realise while both values of x and y are the same, they are **not** pointing to the same 10. They each contain their own version of 10 (and possess their own memory addresses).&#x20;

<details>

<summary>How do you make <code>x</code>  and <code>y</code> to point to the same 10 then?</summary>

Simple: we don't make two variables. We declare 1 variable and multiple pointers to the same variable.

<table><thead><tr><th></th><th></th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">#include &#x3C;iostream>

int main() {
int x = 10;
int\* y = \&x;   // y points to x
int\* z = \&x;   // z also points to x

```
std::cout &#x3C;&#x3C; x &#x3C;&#x3C; std::endl;   // 10
std::cout &#x3C;&#x3C; *y &#x3C;&#x3C; std::endl;  // 10

*y = 20;  // modify via pointer

std::cout &#x3C;&#x3C; x &#x3C;&#x3C; std::endl;   // 20 (x changed!)
```

} </code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FvJWqe76vAFSE7i6ZrKpq%2FScreenshot%202026-04-05%20at%2012.25.38%E2%80%AFAM.png?alt=media&amp;token=1ebdf77d-6ddb-448f-859c-b8aaac179726" alt=""></td></tr></tbody></table>

</details>

### Pointer-Ception

And yes because pointers also have addresses, we can have *pointers that point to a pointer that points to a value.*

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2F7cYKAtgdbhfv6kDn0lUr%2Fimage.png?alt=media&amp;token=21f48cfc-4db5-4f6e-a897-8ca56261aaf5" alt=""><figcaption></figcaption></figure>

<table><thead><tr><th>Code</th><th>Output</th></tr></thead><tbody><tr><td><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp"><strong>#include &#x3C;iostream>
</strong>
int main() {
    int x = 10;            // normal variable
    int* p = &#x26;x;           // pointer to x
    int** pp = &#x26;p;         // pointer to pointer

```
std::cout &#x3C;&#x3C; "x value: " &#x3C;&#x3C; x &#x3C;&#x3C; std::endl;
std::cout &#x3C;&#x3C; "Address of x (&#x26;x): " &#x3C;&#x3C; &#x26;x &#x3C;&#x3C; std::endl;

std::cout &#x3C;&#x3C; "p (points to x): " &#x3C;&#x3C; p &#x3C;&#x3C; std::endl;
std::cout &#x3C;&#x3C; "Value at p (*p): " &#x3C;&#x3C; *p &#x3C;&#x3C; std::endl;

std::cout &#x3C;&#x3C; "pp (points to p): " &#x3C;&#x3C; pp &#x3C;&#x3C; std::endl;
std::cout &#x3C;&#x3C; "Value at pp (*pp): " &#x3C;&#x3C; *pp &#x3C;&#x3C; std::endl;
std::cout &#x3C;&#x3C; "Value at *pp (**pp): " &#x3C;&#x3C; **pp &#x3C;&#x3C; std::endl;

return 0;
```

} </code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FY6ro4t9GBooZVeYnqrKL%2FScreenshot%202026-04-05%20at%2012.27.53%E2%80%AFAM.png?alt=media&amp;token=9cae4917-f800-4514-bc6a-bb00a694b928" alt="" data-size="original"></td></tr></tbody></table>


# Arrays & Strings

Thus far, pointers pointed to a singular variable. But what if we have multiple values stored together (exactly, like an array!)

### Arrays

To declare an array:

{% code title="" %}

```cpp
int arr[3] = {10, 20, 30}; // array
int* p_arr = arr;          // pointer to array
```

{% endcode %}

Visually, this is how an array is represented:

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FlFsr34qQv5tXnXer2WI3%2FScreenshot%202026-04-05%20at%2012.46.02%E2%80%AFAM.png?alt=media&amp;token=3c5de667-eebc-4067-ac54-034ce105c551" alt=""><figcaption></figcaption></figure>

Note that:

* All 3 elements sit **side-by-side** (i.e there exists no gaps between them). This is what we mean by **contiguous memory.**
* You may also have noticed that the difference between each memory address is 4 bytes. This because (usually) `int` occupies 4 bytes.

### Pointer Arithmetic

Since `p_arr`  stores the address of the first element, you can access `arr[0]` via dereferencing `*p_arr`. &#x20;

What about `arr[1]`? The cool thing about pointer arithmetic is that it's **type-aware,** you can simply perform `(p_arr + 1)` to move forward by 1 element of type `int` (i.e 4 bytes).

Functionally,

<table><thead><tr><th width="243.72222900390625">Operation</th><th>Result</th></tr></thead><tbody><tr><td><code>*p_arr</code></td><td>Move <code>p_arr</code> by 0 bytes, dereference to get <code>arr[0]</code></td></tr><tr><td><code>*(p_arr + 1)</code></td><td>Move <code>p_arr</code> by 4 bytes, dereference to get <code>arr[1]</code></td></tr><tr><td><code>*(p_arr + 2)</code></td><td>Move <code>p_arr</code> by 8 bytes, dereference to get <code>arr[2]</code></td></tr></tbody></table>

Note that `arr` also decays into a pointer to the first element that shares the same memory address as `p_arr`, hence operations that can be done with `arr` can also be done with `p_arr`&#x20;

{% code title="main.cp" %}

```cpp
#include <iostream>

int main() {
    int arr[3] = {10, 20, 30};
    std::cout << arr[0] << std::endl; // 10
    std::cout << arr[1] << std::endl; // 20
    std::cout << arr[2] << std::endl; // 30
    
    std::cout << *(arr + 0) << std::endl; // 10
    std::cout << *(arr + 1) << std::endl; // 20
    std::cout << *(arr + 2) << std::endl; // 30
    
    int* p_arr = arr;
    std::cout << *(p_arr + 0) << std::endl; // 10
    std::cout << *(p_arr + 1) << std::endl; // 20
    std::cout << *(p_arr + 2) << std::endl; // 30
}
```

{% endcode %}

<details>

<summary>😎 Cool Fun Fact</summary>

We've learnt that we can access `arr[0]` via `*(arr + 0)`. Since addition is commutative, `*(arr + 0) == *(0 + arr)`, hence `0[arr]` works too!

{% code title="main.cpp" %}

```cpp
#include <iostream>
int main()
{
    int arr[3] = {10, 20, 30};
    std::cout << 1 [arr] << std::endl;
    std::cout << arr[1] << std::endl;
}
```

{% endcode %}

</details>

### Strings

Fundamentally, a string is simply an *array* of characters ending with a special character `\0` (i.e null terminator)

The usual pointer arithmetic rules that we've learnt above apply too.

{% code title="main.cpp" %}

```cpp
#include <iostream>

int main() {
    char str[] = "hello";
    std::cout << str[0] << std::endl;        // 'h'
    std::cout << *(str + 1) << std::endl;    // 'e'
    
    char* str2 = "world";
    std::cout << str2[0] << std::endl;        // 'w'
    std::cout << *(str2 + 1) << std::endl;    // 'o'
}
```

{% endcode %}

Notice when we compile, we get a warning:

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FMpd1gHlJ92BLa1sK7udj%2FScreenshot%202026-04-05%20at%201.17.41%E2%80%AFAM.png?alt=media&amp;token=9a6d054e-e282-488a-ac94-6ced03a517fa" alt=""><figcaption></figcaption></figure>

<details>

<summary>😕 Why the warning for <code>char* str2</code> but not <code>char str[]</code> ?</summary>

`"hello"`  is a string literal stored in read-only memory.

`char[]` has no issues since it copies the string literal onto its own array on the stack.

`char *` is pointing to the same string literal in read-only memory. Writing to it causes undefined behaviour hence the warning.

In fact, if you try compiling the program with stricter flags: `clang++ -std=c++17 -Wall -Wextra -pedantic-errors main.cpp -o hello-world`, the compiler rejects the code entirely.

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2F2Q7C2MtUyScoYSkt1JOT%2FScreenshot%202026-04-05%20at%201.20.16%E2%80%AFAM.png?alt=media&amp;token=42200d2b-3077-4b71-b154-865f5c11172a" alt=""><figcaption></figcaption></figure>

To squash this, we can use `const char *`  to assert that we are not going to modify the string.

</details>


# Memory Model

### Memory Layout

Variables can either be *created* or *destroyed.* They can also exists on different locations (i.e the **stack**, **heap** or **static storage)**&#x20;

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FtMwGQZhaBBgEeXKzji0V%2FScreenshot%202026-04-04%20at%2010.36.44%E2%80%AFPM.png?alt=media&amp;token=fc2f1583-3967-4e79-b33e-3b7a36fa570a" alt=""><figcaption></figcaption></figure>

### Stack vs Heap

#### Stack

Variables created (i.e allocated) on the stack possess automatic storage duration. This means they are:

* allocated when execution enters their scope
* deallocated when out of scope

<details>

<summary>Why is stack allocation fast?</summary>

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FPC4KiPpDlnaA936z7Egx%2Fimage.png?alt=media&amp;token=2d2d6f92-801e-4c6e-af9f-2bd16f13ff91" alt=""><figcaption><p>src: <a href="https://chessman7.substack.com/p/how-your-code-executes-a-guide-to">https://chessman7.substack.com/p/how-your-code-executes-a-guide-to</a> </p></figcaption></figure>

Allocating (and deallocating) variables on the stack is fast as it mainly involves incrementing (and decrementing) the stack pointer.

This is also the reason why uninitialized variables on the stack possess garbage values because the memory they occupy is not automatically zeroed.

</details>

To jog your memory, all the previous variable declarations in  [Variables](/cpp-fundamentals/variables) and  [Pointers](/memory-and-ownership/pointers) were all allocated on the stack.

{% code title="" %}

```cpp
int main () {
    int x = 5;
    int* p = &x;
    int arr[3] = {10, 20, 30};     
    // this copies the string literal onto the array on the stack
    char str[] = "hello";
    
    // the pointer str2 is on stack
    // the string literal world is stored in ROM
    const char* str2 = "world"; 
}
```

{% endcode %}

Consider the following example (please ignore the implementation details of `Person`  class for now)

{% code title="main.cpp" %}

```cpp
#include <iostream>

class Person
{
private:
    int age_;

public:
    Person(int age) : age_(age)
    {
        std::cout << "creating a person with " << this->age_ << std::endl;
    }
    int get_age()
    {
        return this->age_;
    }
    ~Person()
    {
        std::cout << "destroying a person with " << this->age_ << std::endl;
    }
};

int main()
{
    Person p1(10);
    Person p2(15);
    Person p3(20);
}
```

{% endcode %}

The following output is as shown:

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FNwLtyoLw7yV0GfqLm3ri%2FScreenshot%202026-04-04%20at%2010.52.43%E2%80%AFPM.png?alt=media&amp;token=23419b08-c44f-4005-87d0-98891c84c5db" alt=""><figcaption></figcaption></figure>

Observe that similar to the Last-In-First-Out (LIFO) behaviour of literal stack data structure, objects are constructed in order of declaration. They are also destroyed in order of reverse order.

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FpNs0fIDcvdqxfbM9BjCp%2FScreenshot%202026-04-04%20at%2010.57.27%E2%80%AFPM.png?alt=media&amp;token=4ff1726c-a9d4-4d4e-97f5-803618322b29" alt=""><figcaption></figcaption></figure>

Note that the behaviour is tied to scope, objects are destroyed as soon as it exits the scope.

<table><thead><tr><th>Code</th><th>Output</th></tr></thead><tbody><tr><td><p></p><pre class="language-cpp" data-title="main.cpp"><code class="lang-cpp">int main()
{
    Person p1(10);
    {
        Person p2(15);
    }
    Person p3(20);
}
</code></pre></td><td><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2Fj00WbL6i9V4O6QXZf19Q%2FScreenshot%202026-04-04%20at%2010.59.54%E2%80%AFPM.png?alt=media&amp;token=00de9b4d-5ba1-4af9-abd0-dcca180170dc" alt="" data-size="original"></td></tr></tbody></table>

### Heap

While the stack is fast, it does suffer from a few limitations:

* Size of the objects must be known / determinable at compile time
* The lifetime of objects is tied to scope

<details>

<summary>⚠️ More about the limitation</summary>

Consider the following code snippet:

```cpp
int main()
{
    int n;
    std::cin >> n;
    int arr[n];
}
```

It *appears* to work when we try running with `clang++ main.cpp -o hello-world`&#x20;

But look what happens when I try to run with stricter flags:   `clang++ -std=c++17 -Wall -Wextra -pedantic-errors main.cpp`

![](https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FAjTdeO2EBcXf1a9xeFGL%2FScreenshot%202026-04-04%20at%2011.11.25%E2%80%AFPM.png?alt=media\&token=f4313ef7-5286-43c0-9e5c-349675083e8c)

This is because VLA (Variable-Length Arrays) are actually not part of standard C++. Indeed, the size of arrays **must** be known at compile-time.&#x20;

Following the above school of thought, why does the below code still not work? 🤔

```cpp
int main()
{
    int n = 5;
    int arr[n];
}
```

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FpmE29Ybj7do43U15uDzU%2FScreenshot%202026-04-04%20at%2011.27.58%E2%80%AFPM.png?alt=media&amp;token=b64a774d-2b86-4b21-b379-a83c3dc86357" alt=""><figcaption></figcaption></figure>

</details>

You can use a `new` keyword to allocate memory on the heap. Unlike the stack, the heap memory is not tied to scope. It persists until it is explictly deallocated with `delete.`

Let's take a look at how heap tackles these limitations:

{% code title="main.cpp" %}

```cpp
#include <iostream>

int main()
{
    int n;
    std::cin >> n;
    int* arr = new int[n];
    for (int i = 0; i < n; i++) 
        arr[i] = i;
    for (int i = 0; i < n; ++i)
        std::cout << arr[i] << " ";
}
```

{% endcode %}

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2F6WQd7Jddvh7KfVdXcNNe%2FScreenshot%202026-04-04%20at%2011.21.05%E2%80%AFPM.png?alt=media&amp;token=c23d9806-a2d0-4f0d-9e2c-a1ec836b9889" alt=""><figcaption></figcaption></figure>

<details>

<summary>⚠️  <strong>Spot</strong> the error in the above code snippet</summary>

We've just encountered a common pitfall of allocating variables on the heap. The above code snippet is plagued by a **memory leak.**&#x20;

The memory allocated using `new` is never `deallocated` using `delete` .  This means the allocated memory remains reserved after after it's no longer needed.

We can fix the code by appending `delete[] arr;` to the back of the program like so:

{% code title="main.cpp" %}

```cpp
#include <iostream>

int main()
{
    int n;
    std::cin >> n;
    int* arr = new int[n];
    for (int i = 0; i < n; i++) 
        arr[i] = i;
    for (int i = 0; i < n; ++i)
        std::cout << arr[i] << " ";
    delete[] arr;
}
```

{% endcode %}

</details>

#### Dangling Pointers

Another common pitfall that programmers often stumble upon is the issue of dangling pointers. This refers to the situation when we try to access memory that already has been deallocated.

{% code title="main.cpp" %}

```cpp
#include <iostream>
int main() {
    int *p = new int(5);
    std::cout << *p << std::endl;
    delete p;
    std::cout << *p << std::endl; 
}
```

{% endcode %}

The above is an example of [**undefined behavior**](https://stackoverflow.com/questions/28727439/is-it-undefined-behavior-to-dereference-a-dangling-pointer)**.** For me, it prints 0 but the C++ standard makes no guarantees about what happens.&#x20;

<figure><img src="https://1407522535-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgMKUM5bJORzyfGuRLJG7%2Fuploads%2FBLwGc2pJwG2SfeJvrF1S%2FScreenshot%202026-04-04%20at%2011.38.40%E2%80%AFPM.png?alt=media&amp;token=37b9e64e-2b31-42a1-8f9b-3f80aa68df41" alt=""><figcaption></figcaption></figure>

It could also potentially print 5, print garbage values or crash through a segmentation fault.


# Memory Painpoints

Fundamentally, ownership answers the question:&#x20;

> Who is responsible for cleaning up this piece of memory?

As we've seen earlier, memory can be allocated on two distinct regions (i.e the stack and the heap)

### Stack

When variables are allocated on the stack, there's no burden on us to deallocate the memory. C++ handles it for us automatically!

{% code title="main.cpp" %}

```cpp
int main() {
    int x = 10;
} // x is automatically destroyed by end of this scope
```

{% endcode %}

### Heap

#### Exercise

I tried my best to write a code snippet to simulate a game I had in mind. Unfortunately, the code doesn't seem to work for some reason. Do you mind helping me find out where the error(s) are ?

{% code title="main.cpp" %}

```cpp
#include <iostream>

int* spawnScoreBonus() {
    return new int(100); 
}

int* generatePlayerHealth() {
    int* health = new int(50);
    delete health;    
    return health;
}

int main() {
    int* score = spawnScoreBonus();
    std::cout << "Player gained score: " << *score << std::endl;

    int* health = generatePlayerHealth();
    std::cout << "Player health: " << *health << std::endl; 

    int* enemyHp = new int(200);
    int* archerTarget = enemyHp;
    int* knightTarget = enemyHp;
    delete knightTarget;
    delete archerTarget;
    
    return 0;
}
```

{% endcode %}

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="26a0">⚠️</span> Memory Issue 1</summary>

The `spawnScoreBonus()` function allocates a new block of memory containing `100` . Ownership of this object is transferred from `spawnScoreBonus()`  to `main()` .

This means the onus is on `main()` to `delete` the object but `score` is never deallocated, leading to a **memory leak**!

</details>

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="26a0">⚠️</span> Memory Issue 2</summary>

In the `generatePlayerHealth()` function, `health` is deleted before being returned. This means the returned pointer is pointed to free'ed memory.

However, we are still trying to access the value `health` is pointing to in the `main()` function, leading a **dangling-pointer /** **use-after-free.**

</details>

<details>

<summary><span data-gb-custom-inline data-tag="emoji" data-code="26a0">⚠️</span> Memory Issue 3</summary>

Both `archerTarget` and `knightTarget`  both point to the same integer of 200. So both pointers behave like owners and both delete the same blokc of memory.

This leads to a **double-free** scenario where both owners think they own the shared resource.

</details>

As we have seen earlier, even in a small piece of code, there can be many subtle memory issues. Imagine how many of such bugs exist in larger codebases!&#x20;

To tackle this, modern C++ (since C++11) introduced something a little smarter...


# Smarter Pointers

As we've seen, we humans are pretty dumb and inept at managing memory.&#x20;

Here's where smart pointers come in! They are smarter abstractions introduced in C++11 to help us manage memory automatically.

### Unique Pointers

#### Basics

{% code title="main.cpp" %}

```cpp
#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> score = std::make_unique<int>(100);
    std::cout << *score << std::endl;
} // automatically deleted here
```

{% endcode %}

Semantically, unique pointers convey the idea there is **exactly only one owner** of the resource being pointed to.&#x20;

Unique pointers:

* cannot be copied
* can be moved
* are automatically deleted when it goes out of scope

#### Transferring Ownership

{% code title="main.cpp" %}

```cpp
#include <memory>

void consume(std::unique_ptr<int> p) {
    // owns the pointer
}

int main() {
    auto x = std::make_unique<int>(50);
    consume(std::move(x));  // transfer ownership
}
```

{% endcode %}

### Shared Pointers

{% code title="main.cpp" %}

```cpp
#include <iostream>
#include <memory>

int main() {
    auto p1 = std::make_shared<int>(10);
    auto p2 = p1;  // shared ownership

    std::cout << *p1 << std::endl; // 10
    std::cout << *p2 << std::endl; // 10
}
```

{% endcode %}

Both `p1` and `p2` point to the same object. Memory for the integer 10 is only free'ed when both `p1` and `p2` goes out o scope.&#x20;

Internally, a `shared _ptr` uses a reference count to track when it shoud deallocate. We can see the live reference count by invoking `use_count().`

{% code title="main.cpp" %}

```cpp
#include <iostream>
#include <memory>

int main() {
    auto p1 = std::make_shared<int>(10);
    std::cout << p1.use_count() << std::endl; // 1

    auto p2 = p1;
    std::cout << p1.use_count() << std::endl; // 2
}
```

{% endcode %}

### Tackling Our Original Code

#### Exercise

Given the original code in [Memory Painpoints](/memory-and-ownership/memory-painpoints), can you try to use some of these abstractions to create better, memory-safe code?

<details>

<summary>Answer</summary>

{% code title="main.cpp" %}

```cpp
#include <iostream>
#include <memory>

std::unique_ptr<int> spawnScoreBonus() {
    return std::make_unique<int>(100);
}

std::unique_ptr<int> generatePlayerHealth() {
    return std::make_unique<int>(50);
}

int main() {
    std::unique_ptr<int> score = spawnScoreBonus();
    std::cout << "Player gained score: " << *score << std::endl;

    std::unique_ptr<int> health = generatePlayerHealth();
    std::cout << "Player health: " << *health << std::endl;

    std::shared_ptr<int> enemyHp = std::make_shared<int>(200);
    std::shared_ptr<int> archerTarget = enemyHp;
    std::shared_ptr<int> knightTarget = enemyHp;

    return 0;
}
```

{% endcode %}

</details>


