Search code examples
c++structdynamic-arrays

Resize a dynamic array of structures with a function


From what I have read, I should not be having a problem with the code I have. I am reading jumping into C++ by Alex Allain, and he copies dynamic int arrays like this, but when I try to copy a struct array the same way, it give me this error:

Unhandled exception at 0x777108B2 in Friend Tracker.exe: Microsoft C++ exception: std::length_error at memory location 0x006FF838.

From my understanding, this is telling me that I ran out of heap data. However I have 14GB of unused RAM, how is this possible?

When looking at other people's questions with the same error, they were saving multiple copies of arrays 1GB in size.

This leads me to think that its the location in my memory that is the problem, but why is the compiler not allocating the correct amount of heap data in my new array?

struct frnd
{
    string firstName;
    string lastName;
    int lastTalked;
};

int main()
{
    int size = 1;
    frnd* friendsList;

    friendsList = new frnd[2];
    friendsList[1].firstName = "larry";
    frnd* temp;
    temp = new frnd[4];

    for (int i = 1; i < 3; i++)
    {
        temp[i] = friendsList[i];
    }

    delete[] friendsList;

    friendsList = new frnd[4];
    for (int i = 1; i < size; i++)
    {
        friendsList[i] = temp[i];
    }

    delete[] temp;

    cout << "\n\n" << friendsList[1].firstName << "\n\n";
    system("PAUSE");
    return 0;
}

Solution

  • Your errors are due to array indexing in C++ being zero-based, but you are using it like arrays are one-based.

    for (int i = 1; i < 3; i++) {
        temp[i] = friendsList[i];
    }
    

    friendsList points at the first element in a two-element array. This accesses friendsList[1], which is okay. (But note that it is the second element in the array, not the first.)

    On the next iteration it accesses friendsList[2], which would be the third element of the array, but your array only has two elements. This is therefore an out-of-bounds array access, and it causes undefined behavior. In your case, this manifests as an exception by the following sequence of events:

    • Your code tries to copy the friendsList[2] object (a frnd), but this is beyond the end of your array allocation. This is undefined behavior and bad, full stop. However, in the interests of explaining why you get this specific exception...
    • Copying a frnd object uses a default copy constructor, which simply copies each data member of the source object to the target.
    • When the compiler tries to copy friendsList[2].firstName, you're effectively using some other memory adjacent to your allocation as a std::string object, when no such object was constructed in that region of memory.
    • The std::string copy-assignment operator tries to read string-internal data members and gets garbage (uninitialized and/or indeterminate values, the distinction here is pointless because we are already well into UB territory). The first thing it likely tried to do is allocate a char array using the garbage "string length" value from the non-existent source string object, and if that succeeded then it would try to copy that many characters from a region of memory pointed to by an uninitialized pointer. The odds of coming away from this "copy my garbage data as though it were a string object" operation without some kind of exception are very, very low.
      • Another possible outcome: &friendsList[2] could point into a region of memory that hasn't been mapped into your process' address space, triggering an access violation the moment you try to read from it. This would actually be preferable to other things that can happen when you invoke UB.

    You can fix this by adjusting the bounds of your loop: (int i = 0; i < 2; i++)

    Your second loop, on the other hand, does nothing at all because both i and size are 1, so the test i < size fails.


    As pointed out in a comment on your question, you also do not initialize the lastTalked member of your objects before you attempt to copy them. With some exceptions, reading an uninitialized value causes undefined behavior. Therefore, technically even the first loop iteration when i is 1 also invokes undefined behavior, but with your specific compiler and architecture this likely manifests as a copy of an indeterminate value.

    This can be fixed by adding a default frnd constructor that initializes the member:

    struct frnd
    {
        frnd() : lastTalked(0) { }
    
        string firstName;
        string lastName;
        int lastTalked;
    };
    

    Other thoughts on your code:

    • I understand that this code is an exercise. For production code, however, I need to recommend using std::vector instead.
    • using namespace std; is bad practice.