# 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)**

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2F69qXf7mIxHVwYVpi9zJ7%2FScreenshot%202026-04-04%20at%2010.36.44%E2%80%AFPM.png?alt=media&#x26;token=ca75d883-8462-43e9-a870-6d390913c0e7" 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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2Fgit-blob-a9e11b58da5420ee342ca37b0acbbb1ca65f67be%2Fimage%20copy.png?alt=media" 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](https://wiki.nushackers.org/hackerschool/introduction-to-cpp/cpp-fundamentals/variables "mention") and [pointers](https://wiki.nushackers.org/hackerschool/introduction-to-cpp/memory-and-ownership/pointers "mention") 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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2F7pSTGAFJth15qdAMQsQ5%2FScreenshot%202026-04-04%20at%2010.52.43%E2%80%AFPM.png?alt=media&#x26;token=b619d89e-d8e9-45b6-ab87-63117d6961cb" 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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FPB3nVBECjtpL4AfADdqP%2FScreenshot%202026-04-04%20at%2010.57.27%E2%80%AFPM.png?alt=media&#x26;token=e74c09f8-e1bd-4f7b-819d-e88f4726c0ab" 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><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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FFO9Wn8rZbO9FQZaakMt3%2FScreenshot%202026-04-04%20at%2010.59.54%E2%80%AFPM.png?alt=media&#x26;token=bbb9b227-c6ea-4b96-8003-bf720e00097c" 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`

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

![](https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FwfhCvkYdoD8sCTGt2S2s%2FScreenshot%202026-04-04%20at%2011.11.25%E2%80%AFPM.png?alt=media\&token=111d88b4-5ee5-4fa6-96c7-be0ec2fea62b)

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.

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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FjvxCvzcgZ5lczH1VsRP5%2FScreenshot%202026-04-04%20at%2011.27.58%E2%80%AFPM.png?alt=media&#x26;token=8dcb8dcb-29d7-4ae3-be76-30b71d463805" 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://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FrFxG6bloVJGmFwN6WALy%2FScreenshot%202026-04-04%20at%2011.21.05%E2%80%AFPM.png?alt=media&#x26;token=9efd2050-f975-4108-aca1-e06d4c7afc4b" 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.**

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.

<figure><img src="https://2807223923-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTUqAJOgHs57S8lmqdxRV%2Fuploads%2FmJNNulwnnR6jRudw03Su%2FScreenshot%202026-04-04%20at%2011.38.40%E2%80%AFPM.png?alt=media&#x26;token=c7fdb787-198b-4bdb-8b4d-2a597535ab8c" alt=""><figcaption></figcaption></figure>

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