0

I have just done a module on pointers and dynamic memory in C++ and am attempting to complete a personal assignment so that I can practice the concepts. The program manages an array of strings that are names. The goal that I set for myself is that the list is stored in the heap (to practice using "new"), and the list is dynamically sized as new names are entered.

Disclaimer: I realize that this is easily accomplished using vectors, and after struggling with this for hours I re-wrote my original code to use a vector for the list with no problems. However I want to learn where my understanding of how pointers work is broken.

The problem that I have with the program is this: I initialize the name array to have zero elements and have a function to add names that handles the dynamic sizing. When first called it seems to re-size the array correctly and add a new name to the the new array. Within the function to add a name, I can print the contents of the new array. I can also re-assign the old array pointer to the address of the new array on the heap. However when I call the print function from main after adding a name to the list, the list does not contain a name. By my understanding, since I'm using pointers I should be updating values directly, so after the add name function terminates, the values should persist. Also, if I attempt to add a second name the program crashes. What am I doing wrong with the handling of memory?

I've searched quite a bit and the closest that I can find for a resolution was this post:

How to make an array with a dynamic size? General usage of dynamic arrays (maybe pointers too)?

I modified my code based on what I understand from that but it still doesn't work properly.

    #include <stdio.h>
    #include <vector>
    #include <iostream>

    using namespace std;

    void add_name_to_list(string * my_list, size_t * list_size);
    string get_name();
    void print_names(const string *const my_list, const size_t *const list_size);

    int main()
    {
        string *name_list_ptr {nullptr};
        name_list_ptr = new string [0];
        size_t name_list_size{0};
        size_t *name_list_size_ptr {&name_list_size};

        print_names(name_list_ptr, name_list_size_ptr);
        add_name_to_list(name_list_ptr, name_list_size_ptr);
        print_names(name_list_ptr, name_list_size_ptr);
        return 0;
    }

    void add_name_to_list (string * my_list, size_t *list_size)
    {
        string new_name{get_name()};
        string *new_string_ptr{nullptr};
            new_string_ptr = new string [*list_size+1];
            // copy existing list into new list
            cout << "List size is " << *list_size << " so *list size == 0 is " << (*list_size == 0) << endl;
            if(*list_size == 0)
            {
                new_string_ptr[0] = new_name;
                *list_size = *list_size +1;
                cout << new_string_ptr[0] << " has been added to position " << *list_size << endl;

            }
            else
            {
                print_names(my_list, list_size);
                for(size_t i{0}; i < *list_size; i++)
                {
                    cout << "At position " << i << " original list is " << my_list[i] << endl;
                    new_string_ptr[i] = my_list[i];
                    cout << "The name " << new_string_ptr[i] << " has been added to position " << i << " of the new list" << endl;
                }
                new_string_ptr[*list_size - 1] = new_name;
                *list_size = *list_size + 1;
            }
            print_names(new_string_ptr, list_size);

            string *temp_ptr{nullptr};
            temp_ptr = new string [*list_size-1];
            cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
            temp_ptr = my_list;
            cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
            my_list = new_string_ptr;
            delete [] temp_ptr;
            new_string_ptr = nullptr;
            print_names(my_list, list_size);
    }

    string get_name()
    {
        cin.sync();
        cin.clear();
        string new_name{};
        cout << "\nEnter the full name: ";
        getline(cin, new_name);
        cin.sync();
        cin.clear();
        if(new_name.size() <= 1)
            return "0";
        else
            return new_name;    
    }

    void print_names(const string *const my_list, const size_t *const list_size)
    {
        if(*list_size == 0)
            cout << "The list is empty" << endl;
        else
            for(size_t j{0}; j < *list_size; j++)
                    cout << j << ". " << my_list[j] << endl;
    }

One variation that I've tried based on what I learned from searching is:

    cout << "temp ptr is " << temp_ptr << " and my list is " << my_list << endl;
    //my_list = new_string_ptr;
    //delete [] temp_ptr;
    //new_string_ptr = nullptr;

    delete [] my_list;
    my_list = new string[*list_size];
    my_list = new_string_ptr;
    print_names(my_list, list_size);

Unfortunately the results are the same.

A Edwardo
  • 9
  • 3
  • `name_list_ptr = new string [0];` allocates an array of zero size. There's not that much you can do with such an object. You can safely `delete` a `nullptr`, so I'd save the trouble of allocating and having to `delete` an unusable allocation and just leave `name_list_ptr` pointing at `nullptr`. – user4581301 Nov 09 '18 at 00:05
  • When I am faced with a problem like this and I cannot use a `std::vector` I write a simple poor-man's `vector`. Getting even a simple mock `vector` working is quite educational and well worth the time invested. – user4581301 Nov 09 '18 at 00:11
  • @user4581301 Unfortunately I haven't gotten to making object classes yet, so that's going to take a little while. This implementation is actually the pre-cursor to that. – A Edwardo Nov 09 '18 at 03:26

1 Answers1

1

Without checking the logic of the implementation, your list doesn't update because you are assigning my_list = new_string_ptr; but your function received void add_name_to_list (string * my_list, size_t *list_size).

As you are newcomer to C++ world, let me explain clearly: list_size is a pointer to a size_t, so if you modify the pointed memory, the change will persist, but if you modify the pointer itself, it will not.

list_size = new size_t; // This change doesn't persist
list_size++; // This change doesn't persist

*list_size++; // This change persists and the value of pointed memory was increased.

With my_list is happening exactly the same, you are trying to modify the pointer itself, not the pointed memory.

So, you should use:

void add_name_to_list (string * &my_list, size_t *list_size)

Or maybe you are more confortable with

void add_name_to_list (string ** my_list, size_t *list_size)
[...]
*my_list = new_string_ptr;

Hope this helps

LuisGP
  • 431
  • 3
  • 13
  • Supplementary detail to *but if you modify the pointer itself, it will not.* The object pointed at is passed by reference (thanks to the pointer) but the pointer itself is passed by value. `list_size` contains a copy of the address provided by the caller, and changing the copy has no effect on the source. – user4581301 Nov 09 '18 at 00:09
  • Exactly. I didn't want to provide too much detail to don't overflow him with terminology. – LuisGP Nov 09 '18 at 00:13
  • It's a worthwhile concern, but I think we can get away with it here in the comments. – user4581301 Nov 09 '18 at 00:14
  • @LuisGP Thanks for that! I didn't realize that as an object/container pointers could be passed by reference or value... although now that I'm typing that out loud it makes sense. Now that it's working I found another error because I was incrementing the list size too late so I had to move that line of code a couple of lines up. I haven't seen the double asterix before. I'll have to try that. – A Edwardo Nov 09 '18 at 03:31
  • @AEdwardo Please, consider accept my answer as the solution to your question. Accepting correct answers helps to stackoverflow to grow and makes the community stronger :) – LuisGP Nov 09 '18 at 10:30