Intermediate 12 min

Dynamic Memory Allocation

Master dynamic memory allocation with new and delete operators to create variables and arrays at runtime on the heap

Learn how to dynamically allocate memory at runtime using the new and delete operators, giving you control over when memory is allocated and freed.

A Simple Example

#include <iostream>

int main() {
    // Stack allocation (automatic)
    int stackVar{42};  // Created on stack, dies at end of scope

    // Heap allocation (dynamic)
    int* heapVar{new int{42}};  // Created on heap, persists until deleted

    std::cout << "Stack: " << stackVar << "\n";
    std::cout << "Heap: " << *heapVar << "\n";

    delete heapVar;  // MUST free the memory!
    heapVar = nullptr;  // Good practice

    return 0;
}  // stackVar dies here, heapVar was already deleted

Breaking It Down

The new Operator

  • What it does: Allocates memory on the heap and returns a pointer to it
  • Syntax: int* ptr{new int{42}}; for single values, int* arr{new int[10]}; for arrays
  • Remember: Every new must be paired with a delete, or you will leak memory
  • Unlike stack variables, heap memory persists until you explicitly delete it

The delete Operator

  • What it does: Frees memory that was allocated with new
  • Syntax: delete ptr; for single objects, delete[] arr; for arrays
  • Critical rule: Match new with delete and new[] with delete[]
  • Good practice: Set pointer to nullptr after deletion to avoid dangling pointers

Stack vs Heap Memory

  • Stack: Fast, automatic, limited size (usually 1-8 MB), variables die when scope ends
  • Heap: Slower, manual management, much larger, memory persists until you delete it
  • Use stack for: Small, fixed-size data with known lifetime
  • Use heap for: Large data, variable sizes, data that needs to outlive the current scope

Dynamic Arrays

  • What they are: Arrays whose size can be determined at runtime
  • Syntax: int* arr{new int[size]}; where size can be a variable
  • Must use: delete[] arr; to free the entire array properly
  • Remember: In modern C++, prefer std::vector for dynamic arrays - it manages memory automatically

Why This Matters

  • Stack-allocated variables have fixed sizes known at compile time and limited lifetimes. They automatically die when they go out of scope.
  • Dynamic memory allocation lets you create data structures of variable size at runtime, control exactly when memory is freed, implement resizable arrays and complex data structures, and efficiently manage large amounts of data that would overflow the limited stack space.

Critical Insight

When you allocate with new[], you MUST delete with delete[]. Using just delete instead of delete[] on an array causes undefined behavior and potential memory leaks. Similarly, never use delete[] on a non-array allocation!

Think of it like this: new[] and delete[] work as a pair for arrays, while new and delete work as a pair for single objects. Mixing them is like trying to unlock a door with the wrong key - it might seem to work sometimes, but it is dangerous and incorrect.

Best Practices

Always pair new with delete: Every new needs exactly one delete, and every new[] needs exactly one delete[]. No exceptions.

Set pointers to nullptr after deletion: After calling delete ptr;, immediately set ptr = nullptr; to prevent accidentally using a dangling pointer.

Prefer modern alternatives: Use std::vector instead of new[] for arrays, and std::unique_ptr or std::shared_ptr for automatic memory management.

Check allocation success: In critical systems, check if new returns nullptr (when using std::nothrow) to handle allocation failures gracefully.

Common Mistakes

Forgetting to delete: Memory leaks happen when you allocate with new but never call delete. This memory is lost for the lifetime of your program.

Using delete instead of delete[]: Arrays allocated with new[] must be freed with delete[]. Using just delete causes undefined behavior and memory leaks.

Deleting the same memory twice: Double deletion causes undefined behavior and crashes. Always set pointers to nullptr after deletion.

Using memory after delete: Accessing a deleted pointer (dangling pointer) causes undefined behavior. Always set pointers to nullptr after deletion.

Not checking for allocation failure: While rare, new can fail. Use std::nothrow and check for nullptr in critical systems, or use exceptions.

Debug Challenge

This program allocates an array but has a bug when freeing it. Click the highlighted line to fix it:

1 #include <iostream>
2
3 int main() {
4 int* numbers{new int[5]{10, 20, 30, 40, 50}};
5 std::cout << numbers[0] << "\n";
6 delete numbers;
7 numbers = nullptr;
8 return 0;
9 }

Quick Quiz

  1. What is the difference between new and new[]?
`new` for single objects, `new[]` for arrays
No difference
`new[]` is deprecated
  1. What happens if you forget to delete dynamically allocated memory?
Memory leak
Automatic cleanup
Compilation error
  1. Where is dynamically allocated memory stored?
Heap
Stack
Static memory

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once