Search

9.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. 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:

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 9.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.

As of the time of writing, the GCC still has a bug where initializing a dynamically allocated array of chars using a C-style string literal causes a compiler error:

If you have a need to do this on GCC, dynamically allocate a std::string instead (or allocate your char array and then copy the string in).

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 9.4 -- Sorting an array using selection sort and 9.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


9.15 -- Pointers and const
Index
9.13 -- Dynamic memory allocation with new and delete

729 comments to 9.14 — Dynamically allocating arrays

  • Jendri Pitung

    hi Alex and Nascardriver
    i wanna ask you about this snippet of code below:

    it seems to me that we actually don't need to do indirection(putting an '*'/asterisk) in order to access each member of the dynamic array, doesn't it? Why is that so? Why does this work differently compared to when we're accessing single dynamic variable?

    Thanks before!

  • Alan LAI

    Hi, I have a question about c-style string symbolic constants and dynamically allocating array.

    when I try to delete name after newing a piece of memory,the compiler suggest me to use delete instead of delete[]. I undestand name only store the address of the pointer to the only-read string.
    However, if I only delete the pointer to pointer(which is name), will the string itself cause a memory leak?

    • nascardriver

      You're not allocating a string, so there's no string to be deleted. You're allocating a pointer and deleting a pointer.
      A dynamic allocation of a char array initialized from a string literal looks like this

  • Azazel

    I misinterpreted the question, so I went in the "harder way" and actually am glad my code still worked:

    I know I broke the "Don't repeat yourself" rule in the output of the array of strings, but it was just because I didn't find necessary to make a function to it...

  • Adrian Ovidiu

    Hi Alex and nascardriver, this is what I did for the program asked. It works fine but I wanted to know any suggestions so that I can improve this piece of code. I'm an absolute beginner and I had a hard time understanding dynamic memory allocation and how pointers work and I'm starting to understand how it all connects. Thanks for your hard work!

  • TimT

    I think there needs to be a bit more info on using std::sort for the quiz question. The section 9.4 example uses the begin() and end() functions for the arguments, which won't work here with the pointer-to-array, and pretty much every online reference on sort() talks about iterators which are later in the course.

  • Sahil

    “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

    Can you please explain what do you exactly mean when you say that the size isn't accessible to the programmer? For some reason i'm a bit lost here.

    Thanks

  • Burfed

    Pointers in this chapter are all declared as

    , although it was written somewhere that we should stick to

    Is it OK or should I point out this issue in each chapter this occurs?

    • nascardriver

      Thanks for pointing it out. I won't fix the pointers in this lesson, because it's due for an overhaul anyway.

      I suspect there are a lot of lessons where this is "wrong". If you're going to write a comment anyway and you happen to see that the lesson is mis-formatted, please point it out. Don't go out of your way just to write comments about wrong formatting, that'll generate too much work for you as well as us.

  • Patrick

    Also, just curious - what's the difference between std::getline() and std::cin.getline()? Which one should we use, or does it not matter?

  • Patrick

    Back to a previous lesson where you guys explained how array[0] is the same as *(array), does array DECAY when you do array[0]? If so, it would turn into "memory address of first element"[0], which would be the same as *("memory address of first element")?

  • Ivan

    Hi. This's my code for question. I am not sure, is it good or something needs changes?

  • Amin Gholiad

    Today I Learned, that "Unlike C, C++ allows dynamic allocation of arrays at runtime without special calls like malloc() ...". And to my surprise this runs:

    I remember that I had been told this does not compile and I had checked in early 2000s in Turbo c++ and Borland c++.
    Why is this????

  • kio

    Hi Nascardriver and Alex,

    Almost the same solution, I was struggling a bit with std::sort (and calling the std::begin and std::end), instead of sort(<pointer>)

    What do you think?

    • nascardriver

      Hi!

      - Line 16, 17, 30: List initialization
      - Line 17, 30: Pre-increment is cheaper than post-increment
      - `size_t` is C, `std::size_t` from <cstddef> is C++
      - Avoid returning manually allocated memory. The caller of `returnNamesFromUser` can't know that they have to `delete[]` the memory. Keep the `new` and `delete` in the same function if possible.

      • kio

        Thank you nascardriver.
        What book do you recommend for the beginner, Is the "C++ Primer" good enough? I'm on the final year of my CS college, but I had a break for a couple of years due to private and health issues. So I'm getting back to path to finish college, and I'm starting by revising C++ (it's the best starting point for the development).

        • nascardriver

          I can't recommend any books, I don't like books. They get outdated with no way of updating or marking them. You'll read a book not knowing that what you're reading is deprecated or bad practice.

          • kio

            Ah I see. You are right, I have started working with Primer, but I'm following the best practices from here.

            Can you share your dev experience, for sure you are working for 10+ years. It's hard to find guys such as you and alex, to have patience with the students.

            • nascardriver

              Be sure to read up on how C++ has changed since the book's release https://en.cppreference.com/w/cpp/compiler_support

              I'm developing since 2012, professionally since 2020. From my experience so far, developing professionally slows down my learning process, because I don't have as much time to play around and I'm limited in tools, whereas I could do whatever I want for however long it takes non-professionally.

              • kio

                I understand you fully. I'm working in IT industry for 5 years now, but mostly testing and mgmt agile work. When your work for 8 hrs and you have family there is less and less time for yourself. Do you watch f1 besides nascar? I only know Jeff Gordon, number 24. Thank you very much for your time.

  • sk

    Hey!
    I didn't understand std::size_t function. Could you explain that again? And why did we not delete the size_t variable after ending

  • J34NP3T3R

    mine was far from the answer given

    #include <iostream>
    #include <iterator>     // for std::size
    #include <algorithm>    // for std::sort
    #include <string>       // for std::string

    int main()
    {
        std::cout << "How many names you wish ? : ";
        std::size_t nameCount{};
        std::cin >> nameCount;

        std::string *names{ new std::string[ nameCount]{} };
        std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

        for (std::size_t elem{ 0 }; elem <= (nameCount - 1); ++elem)
        {
            std::cout << "\n" << elem + 1 << ". ";
            std::getline(std::cin, names[elem]);
        }

        std::sort(names, names + nameCount);  
        
        for (std::size_t elem{ 0 }; elem <= (nameCount - 1); ++elem)
        {
            std::cout << "\n" << elem + 1<< ". " << names[elem] ;      
        }

        delete[] names;

        return 0;
    }

  • J34NP3T3R

    i was having some difficulties remembering which headers to include for each functions we use like std::string

    then i realized i had std::string typed in without the compiler giving any errors even though i did not have #include <string> typed in.

    how is this possible ?

    ALSO why is it that when i type
    int *ptr
    my compiler automatically moves the * to int
    int* ptr

    • nascardriver

      https://www.learncpp.com/cpp-tutorial/header-files/
      Section "Headers may include other headers"

      That's your auto-formatter forcing you to use a consistent style

  • Will

    I'm slightly confused - for the quiz, I forgot to #include <cstddef> and <string> (I didn't use std::limit in my solution), but my code that had both std::size_t and std::string in it compiled, and ran fine. I then tried removing the <algorithm> and <iostream> headers, and it will only compile if <iostream> is in it, but otherwise it doesn't seem to matter.

    What's going on here? Does <iostream> implicitly #include all of the std namespace? If so, why wouldn't we use something like #include <stdlib> (I don't know if that's a real header, I mean it as an example) to have the whole library at our disposal?

    Thanks!

  • My co-worker showed me how to use

    for strings and how to use it if the comparator is not defined. He also said if you were building this with optimized resource management in mind, put

    at the end of the program right before

    Does that seem about right to you? Also, what's a comparator and when is it used?

    • Ah I see the solution now! I couldn't get std::sort to work without accommodating for the comparator in Virtual Studio 2019! Maybe if I used "auto" for creating and initializing the array this would have worked?

    • Yes, it was creating and initializing the array as type "auto"!

    • nascardriver

      `std::sort` uses `operator<` of the array's type by default. Since `std::string` has `operator<`, you don't need to provide a comparator. You only need to provide a comparator if the type's `operator<` doesn't exist or doesn't do what you want.

      You always need to `delete` things that you created with `new`, no matter if you have resource management in mind or not. Omitting `delete` causes memory leaks and can prevent some types from functioning properly.

  • Got it without looking at the solution :) Took over an hour though experimenting. Tried to find an easy std::sort, but just recycled the bubble sort function from last time.

  • using selection_sort

    #include <iostream>
    #include <algorithm>

    void selection_sort(std::string *data, int length){
        for(int start_index{0}; start_index<length - 1; ++start_index){
            //menampung nilai terkecil
            char smaller = start_index;

            for(int current_index{start_index+1}; current_index<length; ++current_index){
                if(data[current_index] < data[smaller])
                    smaller = current_index;
            }
            std::swap(data[start_index],data[smaller]);
        }
    }

    int main(int argc, char const *argv[]){
        
        std::cout<<"How many names would you like to enter? ";
        size_t name_size{};
        std::cin>>name_size;
        std::string *name{new std::string[name_size]};

        for(int i{0}; i<name_size; ++i){
            std::cout<<"Enter name #"<<i+1<<":";
            // std::getline(std::cin,name[i]);
            std::cin>>name[i];
        }

        std::cout<<"\nHere is your sorted list:\n\n";
           selection_sort(name, name_size);
           //std::sort(name,name+name_size);

        for(int i{0}; i<name_size; ++i){
            std::cout<<"Name #"<<i+1<<':'<<name[i]<<std::endl;
        }

        delete[] name;
        return 0;
    }

  • yeokaiwei

    6.

    This is most confusing as I assumed that we should use the the 2nd line due to "initialize a dynamic array".

    However, the recommended way is auto *array{ new int[5]{ 9, 7, 5, 3, 1 } };

    7. std::sort
    I went to Chapter 6.18, 6.4 to look at the std::sort example which used

    std::sort(std::begin(array), std::end(array));
    instead of
    std::sort(names, names + length);

    This is most confusing to new students as we would use the first pattern taught to us.

    The pattern changes here and examples are not consistent with the Quiz.

    Perhaps, the std::sort(names, names + length); could be highlighted in the std::sort example in 6.18?

    8. Test your skills
    If the intent is to throw a confusing and difficult hoop to jump through, could it be done in a separate final chapter called "Test your skills"?

    "Test your skills" can have the most intricate patterns and students won't have to go through tutorial hell.

    9. Thanks
    Lastly, thanks for the tutorial.

    I hope it can get better in the following years.

  • yeokaiwei

    1. After too many errors, I went to the solution.

    2. // Ignore the line feed that was left by std::cin.
      std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

    Some of the steps are not covered. The solution is quite obfuscated.

    3. Did not consider include #limits at all. Also, added an unnecessary header #include iterator for std::size.

    4. Stack vs Heap
    Perhaps, it would help to point out that
    int is on the stack while
    int* is on the heap?

    Both are on the RAM but they are on different parts. Static memory for int and dynamic memory for int*

    It seems that there is a major difference due to memory allocation.

    Got that from https://www.youtube.com/watch?v=wJ1L2nSIV1s

    • yeokaiwei

      5. Would you be willing to add a 1 minute Youtube videos in between each line?
      I get a feeling that there's only so much you can explain with written text. You can't explain C++ tricks, show diagrams or point out complex examples.

    • nascardriver

      2. Lesson S.4.4b and 5.10
      3. <iterator> is required for `std::size()`.
      4. I don't see how that would be useful. This was mentioned in the previous lesson.

      • yeokaiwei

        4. I don't see how that would be useful. This was mentioned in the previous lesson.

        If you search "int* heap" in the search bar, the earliest reference to it is Chapter 6.16 and the most relevant result is Chapter 7.9.

        If you look at your index, the order is wrong.

        You teach 6.16 AFTER 6.9a.

        That is like not declaring the variable first.

        P.6.9a        Dynamically allocating arrays <<<<<-----
        P.6.10        Pointers and const
        P.6.11        Reference variables
        P.6.11a        References and const
        P.6.12        Member selection with pointers and references
        P.6.12a        For-each loops
        P.6.13        Void pointers
        P.6.14        Pointers to pointers and dynamic multidimensional arrays
        P.6.15        An introduction to std::array
        P.6.16        An introduction to std::vector <<<<<<-----
        P.6.17    

        If you look in the comments, there's a 2015 comment that asked the same question.

  • yeokaiwei

    Feedback.

    If you initialize a dynamic array but you don't reference it.
    Crash.
    Error 0x80072000.

    This results in a peculiar scenario for a newbie as I would write a partial piece of code then debug it.

    However, if I write the partial piece of code, crash.

    Arrays conflict with the normal notion of part by part as there must be a reference to the array, else crash.

  • yeokaiwei

    1. Problem with

    Normal initialize array
    std::array arr{ 13, 90, 99, 5, 40, 80 };

    Dynamic initialize array
    std::cout << "How many names would you like to enter ? " << '\n';
    std::size_t noOfNames{};//Number of names = Length
    std::string* array{ new std::string[noOfNames] };
    std::cin >> noOfNames;//Get the number of names as an integer

    std::sort(arr.begin(), arr.end(), greater);

    std::sort seems to return an error.

    I read the comments and I think someone else had the same problem.

    This is a common error.

  • Glenn

    So, doing this section's quiz question, I had some serious errors getting kicked up, most (not all!) of which seemed to revolve around my use of std::sort. As explained in section 6.4's brief intro to std::sort, I was calling thus:

    yet in particular this was kicking up "A pointer to a bound function may only be used to call the function" error messages, which I tried (but rather failed) to comprehend.

    It took me (on a whim) trying

    without the calls to std::begin() and std::end(), to finally get it to work.

    But can you help me understand why?  And if this is the correct syntax for std::sort, why is it explained differently in 6.4?

    Thanks very much!

    • nascardriver

      `std::begin` and `std::end` only work on built-in arrays and on objects that provide `.begin()` and `.end()` functions or overloads for `std::begin` and `std::end`.
      Your `nameArray` is neither a built-in array nor an object with the functions mentioned above. It's a simple pointer. `std::begin` and `std::end` can't do anything with a pointer.

      `std::sort` (And most other standard algorithms) need to know where your container begins and where it ends. That's exactly what you calculated with `nameArray` and `nameArray + numNames` (This was shown in lesson 6.8a for `std::count_if`).

      • Glenn

        Ah. Thanks for that. Despite being told "A dynamic array starts its life as a pointer that points to the first element of the array," I think I hadn't quite clocked that 'a pointer TO an array' was what I had (unwittingly, it seems) created with

        which made it difficult (read: 'impossible') to recognise that std::begin and std::end were never going to work with it as I'd expected.

        I THINK I get it now. :-P
        Cheers!

  • Okay, dynamically allocated arrays are what I needed for that pig latin translator, how's this?
    piglatin.cpp:

    funcprotos.h:

    • nascardriver

      I'm going to turn it around this time and have you figure out improvements:
      - What is the difference between `std::size_t` and `size_t`?
      - Which header did you include to make `size_t` available?
      - Why is `isVowel()` operating on `std::string`?
      - What happens when you call `isVowel({})`?
      - What there be a difference if `isVowel()` used a `switch`-statement?
      - What happens to your program if the dynamic allocation fails?
      - What happens when you call

      • Left comments in the code saying what I fixed. On that last one, do you mean std::string *arr{ {} } or std::string arr[]{}?
        The std::size(arr) doesn't work on dynamically allocated arrays because it's actually std::size(std::string *) and size() doesn't take that argument. If it's a fixed array, it will work because it decays into a pointer when it's passed to the function, in the function call, the compiler just figures out what the value is that it should be sending to the function, at least that's my best guess.
        piglatin.cpp:

        funcprotos.h:

        • nascardriver

          > - What is the difference between `std::size_t` and `size_t`?
          > - Which header did you include to make `size_t` available?
          Yep

          > - Why is `isVowel()` operating on `std::string`?
          Copying/constructing a string was expensive and is confusing to the caller. When is `isVowel("hello")` true? When there is at least 1 vowel in the string? When all letters are vowels? When the word starts with a vowel? The confusion has been removed by changing the parameter to `char`.

          > - What happens when you call `isVowel({})`?
          This would have caused undefined behavior, because you can't use `.front()` on an empty string. Changing the parameter to `char` prevents this.

          > - [Would] there be a difference if `isVowel()` used a `switch`-statement?
          The `switch`-statement is faster, because the compiler can generate a search tree. When you use the conditional operator or an `if`-statement, the comparisons have to be performed left-to-right.

          > - What happens to your program if the dynamic allocation fails?
          Good fix. Without aborting, the other functions would have resulted in undefined behavior, because you'd be passing in `nullptr`. If you're going to abort anyway, you might as well use the throwing version of `new`. When the exception leaves main, a message will be printed and the user can see that they're out of memory.

          > - What happens when you call
          I meant exactly the code I wrote. `std::string arr[]{ {} };` is an array with 1 list-initialized `std::string`. Now that I read it again, it would've been clearer to use `std::string arr[1]{};`. This array is not dynamically allocated, so `std::size()` works. Calling `translateWords` with empty strings causes undefined behavior.

  • I am able to use 'std::numeric_limits' without #include-ing <limits>. Why is that?

  • Uyph

    Hi, in lesson 6.4, you wrote the code for std::sort like this:

    But when i attempted it for quiz #1 here

    an error of 'no matching function' came up, could you help me?

    • nascardriver

      You cannot use `begin`/`end` on arrays that have decayed to a pointer. Calculate begin and end using pointer arithmetic

      • Uyph

        Thank you, but i'm still confused as to what kind of arrays can decay to pointers. I see in the 6.4 example the array is fixed but it hasnt been used in an expression so it didnt decay. Is that what it is?

        • nascardriver

          Yes. Arrays decay when they are used. `begin` and friends use templates, that's why they can use the array without it decaying. Templates are covered later.
          When you dynamically allocate an array, it's always a pointer.

  • Umut

    My solution,

  • alistair

    Can someone tell me how this program would work, if we wanted to enter more information?

    So instead of just having the name, maybe name, favorite color, and random age.

    So the program will ask, how many people?

    so, lets say 2.

    Then it would it run like so...

    "Enter name of one person:"

    "Enter their favorite color:"

    then to the next person...

    "Enter name of next person:"
    "Enter their favorite color:"

    then it would just print that information with a random age...

    Name: Joe
    Color: Blue
    Weight 125lbs

    Name: Mark
    Color: Green
    weight: 220lbs

    Any idea? I've been working on something like this...thanks.

Leave a Comment

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