Search

10.9 — 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 std::make_unique or std::make_shared. To keep things clear, we’ll be using std::make_unique only. std::make_shared works exactly the same.

Warning

std::make_shared only started supporting arrays in C++20. Before C++20, some compilers falsely allow std::make_shared to be used with arrays, but the allocated memory will not be freed correctly. Only use std::make_shared with arrays after making sure you are using C++20 or later.

See the “Extending std::make_shared() to support arrays” row on the compiler support section of cppreference to check if your compiler can handle arrays in std::make_shared.

Output

5 0 0 0 0 

Because we are allocating an array, C++ knows that it should use the array version of std::make_unique instead of the scalar version. In the array version of std::make_unique, the array cannot be initialized. We have to tell std::make_unique the length of the array and all elements get value-initialized (Initialized to a safe default value, in this case 0).

The length of dynamically allocated arrays has to be a type that’s convertible to std::size_t. We could use int, but that would cause a compiler warning when the compiler is configured with a high warning level. We have the choice between using std::size_t as the type of length, or declaring length as an int and then casting it when we create the array like so:

To avoid the lengthy cast, we’re using std::size_t directly.

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

std::unique_ptr takes care of freeing the memory for us when we use std::make_unique to allocate an array, just like it freed the memory for us when we allocated individual variables.

One often asked question about dynamically allocated arrays is, “How does std::unique_ptr know how much memory to delete? The length of the array is unknown!” The answer is that dynamically allocated arrays keep track of how much memory was allocated, so that the proper amount of memory can be deleted without specifying the length. Unfortunately, this size/length isn’t accessible to the programmer. If we want to know the length of an array, we have to keep track of it ourselves.

Dynamic arrays are almost identical to fixed arrays

In lesson 6.8 -- 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.

Initializing dynamically allocated arrays

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

All of the elements in array are 0. If you want to initialize the elements to specific values, as was possible with fixed-length arrays like so

you’re out of luck. std::make_unique can’t perform any initialization of arrays apart from value-initialization to a default value. std::make_shared can initialize all elements to a specific value, but not selectively. It either value-initializes all elements, or initializes all elements to the same value. To fill a unique array with values, the values have to be assigned to the array later. This isn’t too big of an issue. After all, if we knew what to initialize the array with, we’d know how long the array is, so we wouldn’t need to dynamically allocate the array in the first place.

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 and using a standard container instead. To get a better understanding of standard containers, we’ll implement class similar to std::vector in the next lessons.

Conclusion

Dynamic memory allocation can be used to create arrays at run-time, but working with dynamically-allocated arrays can be tricky. Fortunately, most of the containers that you need in day-to-day programming are available in the standard library and dynamically-allocated arrays can be avoided.

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 6.4 -- Sorting an array using selection sort and 6.8a -- 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

When passing a std::unique_ptr to a function, use get() to get a regular pointer to the data. To the function it doesn’t matter if the array is dynamically allocated or not, it only needs a pointer. The pointer returned by get() can also be used to perform pointer arithmetic. This cannot be done on std::unique_ptr directly.

Show Solution

Question #2

Update the code from the previous quiz to use std::vector instead of a dynamically allocated array. Your updated code should not use any pointers.

Show Solution

%Missing lookup for lesson id 2399%

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">