Dynamic Memory and RAII

Dynamically Allocated Arrays

An ordinary array has to have a predetermined length at compile time. If the number of elements can only be known at runtime, or you want your array to have a lifetime longer than the block in which it was created, the program will allocate the array on the heap.

The statement doing the allocation is: new followed by the type of the element and a size in brackets. So it returns a pointer to the first element.

#include <iostream>

int main()
{
    int length { 4 };
    int* arr { new int[length] };

    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 30;
    arr[3] = 40;

    std::cout << arr[0] << '\n';
    std::cout << arr[1] << '\n';
    std::cout << arr[2] << '\n';
    std::cout << arr[3] << '\n';

    // Release the dynamically allocated array
    delete[] arr;

    return 0;
}

The variable length contains how many integers are needed. Because new int[length] asks for a block of memory large enough to contain length integers, and asks for the address of the first element of that block, the pointer arr is assigned to have that address. Each element is stored into the array by using array index notation and later retrieved in that way.

When the array is no longer needed, delete[] arr is called to destroy every element and return the whole block of memory to the heap. The brackets after delete are necessary, for they tell the language that a pointer points to an array.

If size is available during allocation, elements can be initialized using the same expression.

#include <iostream>

int main()
{
    // Allocate this object dynamically
    int* arr { new int[3]{ 5, 15, 25 } };

    std::cout << arr[0] << '\n';
    std::cout << arr[1] << '\n';
    std::cout << arr[2] << '\n';

    delete[] arr;

    return 0;
}

The braced list provides the initial values written into the 3 integers when the memory is acquired. The pointer arr once again has the address of the first element, and the three std::cout statements read through that pointer. The corresponding delete[] releases that memory once it is no longer needed.

The new[] operator returns a pointer, but this pointer has no knowledge of how many objects have been allocated to it. You need to store that information yourself, usually in a separate int variable. Without the length, there's no safe way to know how far the array extends.

#include <iostream>

int main()
{
    int length { 5 };
    int* arr { new int[length]{ 1, 2, 3, 4, 5 } };

    // Increment the value before the expression uses it
    for (int index { 0 }; index < length; ++index)
    {
        std::cout << arr[index] << '\n';
    }

    delete[] arr;

    return 0;
}

The test part of the loop uses the same length given to the new. The pointer points to the elements one by one at each iteration. Running the loop past length would fetch data outside of the array, which is undefined behavior.

After delete[] has been called, the pointer still points to where the memory was previously allocated, but that address no longer points to valid memory. Any subsequent referencing of the pointer would be undefined behavior.

#include <iostream>

int main()
{
    int* arr { new int[2]{ 7, 8 } };

    delete[] arr;

    // std::cout << arr[0] << '\n'; // Undefined behavior

    return 0;
}

The commented line accesses a storage that has been returned to the heap before. The pointer is set to null to avoid any reuse of this storage.

#include <iostream>

int main()
{
    int* arr { new int[2]{ 7, 8 } };

    delete[] arr;
    // Use nullptr when the pointer refers to no object
    arr = nullptr;

    if (arr)
    {
        std::cout << arr[0] << '\n';
    }

    return 0;
}

Once assigned to null, the test always fails and the subscript is never tested. The delete of a null pointer is defined to be a no op, so a pointer variable can be safely deleted repeatedly, as long as it's null, which is set by delete.

Another reason for undefined behavior is mismatched use of delete. Memory allocated with new[] should be deallocated with delete[] and memory allocated with normal new should be deallocated by normal delete. Choosing the incorrect delete leads to undefined behavior as the language does not know whether to destruct an individual object or destroy the entire array.

Exercise: Dynamically Allocated ArraysStarter code, hints and solutionOpenClose
Guided practiceBeginner · 10–15 min

Practice: Dynamically Allocated Arrays

Work through the mission, reveal hints only when needed, and compare your result with the model solution.

Your challenge

Change arr[2] to 77 before the outputs. Predict the four values, keep all indexes within length, and retain delete[] after the last use.

Mission checklist

  1. Run the starter as a baseline, then predict the change requested in Your challenge.
  2. Make that change in your editor; use the lesson-specific hints if you need help.
  3. Compile and run your answer, then compare with the worked solution and explain the result.

Starter program

This is the working baseline, not the completed answer. Compile as C++20, then make the change described above.

#include <iostream>

int main()
{
    int length { 4 };
    int* arr { new int[length] };

    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 30;
    arr[3] = 40;

    std::cout << arr[0] << '\n';
    std::cout << arr[1] << '\n';
    std::cout << arr[2] << '\n';
    std::cout << arr[3] << '\n';

    // Release the dynamically allocated array
    delete[] arr;

    return 0;
}

Hints

Hint 1

new int[length] allocates length elements without initializing their int values. This example assigns all four elements before reading any of them.

Hint 2

Keep every index below length and pair the array allocation with delete[]. If you increase length and read a new element, initialize that element first.

Worked solution

arr[2] is changed to 77 after initialization and before any output. Index 2 selects the third of four allocated elements. The matching delete[] remains after all reads.

#include <iostream>

int main()
{
    int length { 4 };
    int* arr { new int[length] };

    arr[0] = 10;
    arr[1] = 20;
    arr[2] = 77;
    arr[3] = 40;

    std::cout << arr[0] << '\n';
    std::cout << arr[1] << '\n';
    std::cout << arr[2] << '\n';
    std::cout << arr[3] << '\n';

    // Release the dynamically allocated array
    delete[] arr;

    return 0;
}

Example output

10
20
77
40

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.