Dynamic Memory Allocation in C++

When you declare a regular variable like int x = 5;, memory is reserved for it at compile time and released automatically when the variable goes out of scope. This is called stack memory. But what if you do not know how much memory you need until the program is actually running? That is where dynamic memory allocation comes in.

Dynamic memory allocation lets your program request memory during runtime from a region called the heap. You decide exactly how much memory to grab and when to release it. This gives your program great flexibility — but also places the responsibility of cleanup on you.

Stack vs Heap — The Key Difference

┌──────────────────────────────────────────────────────────┐
│                       MEMORY                             │
├──────────────────┬───────────────────────────────────────┤
│   STACK          │   HEAP                                │
│                  │                                       │
│ • Fixed size     │ • Large, flexible size                │
│ • Auto-managed   │ • Manually managed (new / delete)     │
│ • Fast access    │ • Slightly slower access              │
│ • Local vars     │ • Dynamic arrays, objects             │
│ • Auto-released  │ • Released only when you say so       │
└──────────────────┴───────────────────────────────────────┘

Allocating Memory with new

The new operator requests memory from the heap and returns a pointer to that memory.

Syntax:

data_type* pointer = new data_type;

Example — Single Variable:

#include <iostream>
using namespace std;

int main() {
    int* p = new int;     // request memory for one int on the heap
    *p = 42;              // store a value in that memory

    cout << "Value: " << *p << endl;
    cout << "Address: " << p << endl;

    delete p;             // release the memory
    p = nullptr;          // good practice: avoid dangling pointer
    return 0;
}

Output:

Value: 42
Address: 0x1a2b3c4d (varies each run)

Initialize at Allocation:

int* p = new int(100);   // allocate and initialize to 100

Releasing Memory with delete

Every block of memory you allocate with new must be released with delete when you are done using it. Failing to do this causes a memory leak — the memory stays occupied for the entire lifetime of the program even though you no longer need it.

int* p = new int(55);
// ... use p ...
delete p;       // free the memory
p = nullptr;    // reset pointer to safe state

Dynamic Arrays

A major use of dynamic allocation is creating arrays whose size is determined at runtime. Regular arrays require a fixed, compile-time size. Dynamic arrays do not.

Syntax:

data_type* array = new data_type[size];

Example:

#include <iostream>
using namespace std;

int main() {
    int n;
    cout << "How many numbers? ";
    cin >> n;

    int* arr = new int[n];    // allocate n integers on the heap

    for (int i = 0; i < n; i++) {
        arr[i] = (i + 1) * 10;
    }

    cout << "Values: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    delete[] arr;    // use delete[] for arrays (NOT delete)
    arr = nullptr;
    return 0;
}

Sample Output (n = 4):

How many numbers? 4
Values: 10 20 30 40

Always use delete[] (with brackets) for dynamically allocated arrays. Using plain delete on an array causes undefined behavior.

Dynamic 2D Arrays

Creating a 2D array dynamically requires allocating an array of pointers, then allocating each row separately.

#include <iostream>
using namespace std;

int main() {
    int rows = 3, cols = 4;

    // Step 1: allocate array of row pointers
    int** matrix = new int*[rows];

    // Step 2: allocate each row
    for (int i = 0; i < rows; i++) {
        matrix[i] = new int[cols];
    }

    // Fill and print
    int val = 1;
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            matrix[i][j] = val++;
            cout << matrix[i][j] << "\t";
        }
        cout << endl;
    }

    // Step 3: free each row first, then the pointer array
    for (int i = 0; i < rows; i++) {
        delete[] matrix[i];
    }
    delete[] matrix;
    matrix = nullptr;
    return 0;
}

Output:

1    2    3    4
5    6    7    8
9    10   11   12

Memory Allocation for Objects

new also works for objects. It calls the class constructor automatically. delete calls the destructor.

#include <iostream>
using namespace std;

class Car {
public:
    string brand;
    Car(string b) : brand(b) {
        cout << brand << " created." << endl;
    }
    ~Car() {
        cout << brand << " destroyed." << endl;
    }
};

int main() {
    Car* c = new Car("Toyota");   // constructor called
    cout << "Using: " << c->brand << endl;
    delete c;                     // destructor called
    return 0;
}

Output:

Toyota created.
Using: Toyota
Toyota destroyed.

Common Dynamic Memory Problems

┌─────────────────────┬────────────────────────────────────────────────┐
│ Problem             │ Description                                    │
├─────────────────────┼────────────────────────────────────────────────┤
│ Memory Leak         │ new without delete — memory wasted forever     │
│ Dangling Pointer    │ Using a pointer after delete — crash/corruption│
│ Double Delete       │ Calling delete twice — undefined behavior      │
│ Delete vs Delete[]  │ Using delete instead of delete[] on an array   │
│ Null Dereference    │ Using a nullptr pointer — program crash        │
└─────────────────────┴────────────────────────────────────────────────┘

Checking if Allocation Succeeded:

int* p = new(nothrow) int[1000000000];   // huge allocation
if (p == nullptr) {
    cout << "Memory allocation failed!" << endl;
} else {
    // use p
    delete[] p;
}

new vs malloc — C++ vs C Style

Featurenew (C++)malloc (C)
LanguageC++C and C++
Type safetyYes (returns typed pointer)No (returns void*)
Calls constructorYesNo
On failureThrows exception (or nullptr with nothrow)Returns NULL
Pair withdelete / delete[]free()

Always prefer new and delete in C++ code. Use smart pointers (covered in a later topic) for even safer memory management.

Key Takeaways

  • Dynamic allocation lets you request memory at runtime using new.
  • Memory from new must be released with delete to avoid memory leaks.
  • Use new[] and delete[] for arrays — never mix them with the non-array versions.
  • Set pointers to nullptr after deleting to avoid dangling pointer bugs.
  • Dynamic allocation is essential when the size of data is not known at compile time.

Leave a Comment

Your email address will not be published. Required fields are marked *