1

So I've been returning to the world of C/C++ after many years of working in Java/C# and Python. I've been a software engineer for the better part of 12 years so I'm familiar with value vs reference and design patterns, but this issue I've been seeing has made me scratch my head and question myself.

I've been following a primer guide to get myself back up to speed with C/C++, starting from the basics including pointers. I've been working on dynamic arrays and memory management. I know... I know... in production situations I'm better off using vectors, but this is for education and catching up. So on to the problem...

Here is sample code:

int main() {
    int capacity = 1;
    int size;
    int * dynamicArray = CreateDynamicArray(capacity, size);

    InsertElement(dynamicArray, 20, size, capacity);

    ResizeDynamicArray(dynamicArray, size, capacity + 1);

    delete[] dynamicArray
    return 0;
}

int * CreateDynamicArray(int capacity, int& size){
    size = 0;
    cout << "Creating Dynamic Array of size: " << capacity << endl;
    return new int[capacity];
}

void InsertElement(int* dynamicArray, int element, int& size, int& capacity){
    dynamicArray[size] = element;
    size++;
}

void ResizeDynamicArray(int* dynamicArray, int& size, int newCapacity){
    int * temp = new int[newCapacity];
    copy(dynamicArray, dynamicArray + size, temp);
    delete[] dynamicArray;
    dynamicArray = temp;
}

So here we are using the new/delete method of memory allocation (will move to alloc/free later if I need to). When I write this code all into the main function, meaning no other functions, everything works perfectly fine. But when I break the code into separate functions I get really weird behavior with the memory.

in ResizeDynamicArray() I create the temp pointer and use std::copy to move data into the resized array. I then delete the old pointer and then point to the new array. However, while watching the pointer and outing the value, after the function returns the pointer's reference value dynamicArray[0] goes from 20 to some random value, as if the pointer was deleted. If I remove the delete[] dynamicArray portion it doesn't have this issue.

So this leaves me thinking, why does deleting the old pointer affect the new address that I am now pointing to? Why does this behavior only happen when done within a function? How do I best avoid this?

Using double pointers int** dynamicArray seems to avoid this issue, but I'm having a hard time understanding why as you have to deference back to the first pointer anyways.

Stealthguy
  • 63
  • 6
  • I get what you're trying to do, work up from the fundamentals, but keep in mind learning to do *effective* C++ is largely about avoiding things like `new`. Aside from that, do get in the habit of passing in arguments as `const` and *strictly* following things like the [Rule of Three](https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)). What you have here should be a `class` or a `struct` or all you're doing is just C with extra steps. **Do C, or do C++.** Don't think there's a thing like C/C++ you can actually do. C++ has vastly different tools than C. – tadman Jan 25 '23 at 01:18
  • appreciate that. I know there are a ton of things that C++11 does to make life easier, but I think it's necessary for me to be able to at least understand lower level implementations of such things that c++11 does for you. I do think that making this a class would put everything into better scope for memory management, but what exactly causes this behavior? – Stealthguy Jan 25 '23 at 01:26
  • You'll need to pay *extremely* close attention to how you pass arguments, in particular pointers. If you want to manipulate something outside of a function, you likely need a reference to a pointer (C++, ugly and avoidable) or a pointer to a pointer (C, ugly but how it is). Here you trash the array given to you, assign the new value to a local pointer, and then effectively throw that into the sun. *Undefined behaviour* ensues. – tadman Jan 25 '23 at 01:38
  • If you consolidate this into a `class` then you won't have to pass all this around, it'll all be available as if local variables, they're part of the function's scope. – tadman Jan 25 '23 at 01:39

1 Answers1

2

The line dynamicArray = temp; does not have the effect you are hoping for. dynamicArray is a local variable, changing its value has no visible effect to the caller. Since it is the last line in the function it has no visible effect at all.

For this line to make sense, you would have to pass dynamicArray by reference, i.e.:

void ResizeDynamicArray(int*& dynamicArray, int& size, int newCapacity)
Chronial
  • 66,706
  • 14
  • 93
  • 99
  • Apologies, I made a mistake in my code that I added here! That's on me, my bad. I'm not passing the pointer reference. Updated the initial code. – Stealthguy Jan 25 '23 at 01:10
  • My answer still stands and is still the cause of your issue. – Chronial Jan 25 '23 at 01:23
  • That's interesting. I had thought the default behavior that pointers are passed by reference. But when I think about it it makes sense. Without the reference syntax it is a local pointer that points to the memory position so you can modify the elements of the array but not the array itself. Watching the address closely this time I noticed the memory address goes back to the older, deleted address after the function and I just didn't catch it like a doof. Thanks for the clarification, I'll not make that mistake again. – Stealthguy Jan 25 '23 at 01:38
  • Nothing is passed by reference in C++ unless you ask for it to be passed as a reference, as in an `&` in the argument type. Other languages do this implicitly, but not C++. It's values unless otherwise specified. – tadman Jan 25 '23 at 01:39
  • *I had thought the default behavior that pointers are passed by reference* -- Everything in C++ is passed by-value, unless you *explicitly* specify that the argument is a reference. `void foo(int *x) { x = new int; } int main() { int *p = nullptr; foo(p); }` you will see that `p` remains null, even though it was passed to `foo`. – PaulMcKenzie Jan 25 '23 at 01:40
  • Yeah, I get that, but technically you are passing the address as a value and thus you can modify what is in that address, but not the pointer itself. I tripped myself up thinking that I could modify what the original pointer was pointing too, but giving it some guided thought it definitely makes sense and is facepalm worthy. Apologies haha! – Stealthguy Jan 25 '23 at 01:43