10.14 — Dynamically allocating arrays

In addition to dynamically allocating single values, we can also dynamically allocate arrays of variables. Unlike a fixed array, where the array size must be fixed at compile time, dynamically allocating an array allows us to choose an array length at runtime.

To allocate an array dynamically, we use the array form of new and delete (often called new[] and delete[]):

Because we are allocating an array, C++ knows that it should use the array version of new instead of the scalar version of new. Essentially, the new[] operator is called, even though the [] isn’t placed next to the new keyword.

The length of dynamically allocated arrays has to be a type that’s convertible to std::size_t. In practice, using an int length is fine, since int will convert to std::size_t.

Author's note

Some might argue that because array new expects a length of type size_t, our lengths (e.g. such as length in the example above) should either be of type size_t or converted to a size_t via static_cast.

I find this argument uncompelling for a number of reasons. First, it contradicts the best practice to use signed integers over unsigned ones. Second, when creating dynamic arrays using an integral length, it’s convention to do something like this:

5 is an int literal, so we get an implicit conversion to size_t. Prior to C++23, there is no way to create a size_t literal without using static_cast! If the designers of C++ had intended us to strictly use size_t types here, they would have provided a way to create literals of type size_t.

The most common counterargument is that some hyper-pedantic compiler might flag this as a signed/unsigned conversion error (since we always treat warnings as errors). However, it’s worth noting that GCC does not flag this as a signed/unsigned conversion error even when such warnings (-Wconversion) are enabled.

While there is nothing wrong with using size_t as the length of a dynamically allocated array, in this tutorial series, we will not be pedantic about requiring it.

Note that because this memory is allocated from a different place than the memory used for fixed arrays, the size of the array can be quite large. You can run the program above and allocate an array of length 1,000,000 (or probably even 100,000,000) without issue. Try it! Because of this, programs that need to allocate a lot of memory in C++ typically do so dynamically.

Dynamically deleting arrays

When deleting a dynamically allocated array, we have to use the array version of delete, which is delete[].

This tells the CPU that it needs to clean up multiple variables instead of a single variable. One of the most common mistakes that new programmers make when dealing with dynamic memory allocation is to use delete instead of delete[] when deleting a dynamically allocated array. Using the scalar version of delete on an array will result in undefined behavior, such as data corruption, memory leaks, crashes, or other problems.

One often asked question of array delete[] is, “How does array delete know how much memory to delete?” The answer is that array new[] keeps track of how much memory was allocated to a variable, so that array delete[] can delete the proper amount. Unfortunately, this size/length isn’t accessible to the programmer.

Dynamic arrays are almost identical to fixed arrays

In lesson 10.10 -- Pointers and arrays, you learned that a fixed array holds the memory address of the first array element. You also learned that a fixed array can decay into a pointer that points to the first element of the array. In this decayed form, the length of the fixed array is not available (and therefore neither is the size of the array via sizeof()), but otherwise there is little difference.

A dynamic array starts its life as a pointer that points to the first element of the array. Consequently, it has the same limitations in that it doesn’t know its length or size. A dynamic array functions identically to a decayed fixed array, with the exception that the programmer is responsible for deallocating the dynamic array via the delete[] keyword.

Initializing dynamically allocated arrays

If you want to initialize a dynamically allocated array to 0, the syntax is quite simple:

Prior to C++11, there was no easy way to initialize a dynamic array to a non-zero value (initializer lists only worked for fixed arrays). This means you had to loop through the array and assign element values explicitly.

Super annoying!

However, starting with C++11, it’s now possible to initialize dynamic arrays using initializer lists!

Note that this syntax has no operator= between the array length and the initializer list.

For consistency, fixed arrays can also be initialized using uniform initialization:

Explicitly stating the size of the array is optional. Doing so can help catching errors early, because the compiler will warn you when the specified length is less than the actual length.

Resizing arrays

Dynamically allocating an array allows you to set the array length at the time of allocation. However, C++ does not provide a built-in way to resize an array that has already been allocated. It is possible to work around this limitation by dynamically allocating a new array, copying the elements over, and deleting the old array. However, this is error prone, especially when the element type is a class (which have special rules governing how they are created).

Consequently, we recommend avoiding doing this yourself.

Fortunately, if you need this capability, C++ provides a resizable array as part of the standard library called std::vector. We’ll introduce std::vector shortly.

Quiz time

Question #1

Write a program that:
* Asks the user how many names they wish to enter.
* Dynamically allocates a std::string array.
* Asks the user to enter each name.
* Calls std::sort to sort the names (See 10.4 -- Sorting an array using selection sort and 10.11 -- Pointer arithmetic and array indexing)
* Prints the sorted list of names.

std::string supports comparing strings via the comparison operators < and >. You don’t need to implement string comparison by hand.

Your output should match this:

How many names would you like to enter? 5
Enter name #1: Jason
Enter name #2: Mark
Enter name #3: Alex
Enter name #4: Chris
Enter name #5: John

Here is your sorted list:
Name #1: Alex
Name #2: Chris
Name #3: Jason
Name #4: John
Name #5: Mark

A reminder

You can use std::getline() to read in names that contain spaces.

A reminder

To use std::sort() with a pointer to an array, calculate begin and end manually

Show Solution

10.15 -- Pointers and const
10.13 -- Dynamic memory allocation with new and delete

743 comments to 10.14 — Dynamically allocating arrays

  • Aasif Ali

    why are we using std::size_t everywhere instead of int ?
    can anybody explain?

  • Ridwan Tahir

    Memory Leak?


  • Waldo Lemmer

    - operator* should be next to the type in:

      - introduction
        - code block 1, line 10:

        - code block 2, line 3:


      - Section "Initializing dynamically allocated arrays"
        - code block 1, line 1:


        - code block 2, line 1:


        - code block 3
          - line 2:


          - line 4:


    - Section "Initializing dynamically allocated arrays" contains C++11
      references, but I think they should stay

    - Question #1's solution:
      - uses 2-space indentation
      - What if operator`new[]` fails? See previous lesson, section "Operator new can fail"

  • ArmandoIG

    Hello, thank you very much for this website! I made my code and it worked, I'll paste it for feedback but I have a few questions:

    First, I didn't use pointers directly cuz I feel more confortable with array syntax but I noticed std::sort didn't work even if I used std::begin() and std::end() like in previous lessons, was it better to work with pointers directly?

    Also, I used int instead of size_t and it worked perfectly, is something wrong using int even though it implicitly converts it?

    Extra Question: I like to write functions below main and forward declare them, is that ok? I think it looks cleaner...

    My last question if there is there a way to enter invalid input to std::getline that can mess the buffer?

    Thank you in advance.

    • nascardriver


      > feedback
      If you use the initial value of a variable, explicitly initialize it to that value to make the code easier to understand

      Looks pretty good :)

      > std::sort didn't work even if I used std::begin() and std::end()
      `std::begin()` and `std::end()` work on arrays, but `dNames` is not an array, it's a mere pointer. It doesn't have length information associated with it, there's no way for `std::end()` to know where the end is. If you only have a pointer, you have to calculate the end manually.

      > I used int instead of size_t and it worked perfectly
      You didn't enable sign conversion warnings. I'm not currently sure myself if int or std::size_t is better.

      > I like to write functions below main and forward declare them, is that ok?
      It's more effort. If you're fine with it that's cool.

      > is there a way to enter invalid input to std::getline that can mess the buffer?
      Not that I know of, no

  • UnknownLearner

    Hi, my example for Quiz. Guys, thanks for this site!

  • ved9rakash

    Found a usage for initializing dynamic allocated array.

    This prints:

    by this we can dynamically allocate the array by setting up starting values.

  • James

    I believe that we can now initialize a dynamic array of chars with a string literal with GCC.

    will compile.
    Tested in GCC 11.1.1
    Also: ("Fixed in GCC 11")!

  • Jurbit

    If we were to omit the * next to auto would that change anything, or does auto know that names is a pointer because it's being initialized with new?

    I was able to run it and compile the program without the * but I wasn't sure what that does under the hood.


Leave a Comment

Put all code inside code tags: [code]your code here[/code]