template <typename TT>
//pass the adress of the first element of the array and the new size as params
void increaseArrayDepth(TT * array, unsigned int newSize) //newSize should be bigger as the old size
{
TT * dummyThingy = &array[0]; //store the pointer to the first element
delete array; //delete the old array
array = new TT[newSize]; //create a new one with the fitting size
array = dummyThingy;
};
Resizing arrays
Hello,
as C++ does not directly support dynamic arrays (I mean to change their size when the program runs), I have written the following function to help me out:
I have tested this and it works. But my question is: If I have an array with 5 elements and I resize it to 4 elements, what happens to the fifth one? Does it still exist, but takes away memory or is it deleted somehow?
Thanks in advance,
Marco
if you want resizable arrays, use something like std::vector (#include <vector>
Your function has undefined behaviour:
As an example, here's what happened when I tried to use your code:
I think you'll agree that should print 7 right? It doesn't. It prints 9.
---------------------------------------------
EDIT: Just to make it clear what's happening. Image you have memory like this:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
where each box is a byte.
You start your program and execute:
int* array = new int[2];
This allocates an array of two element from memory, i.e.:
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | |
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
^ ^
array
(the *'s indicate that the memory has been allocated)
Next you write the value 7 to these two locations:
array[0] = 7;
array[1] = 7;
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
^ ^
array
Now you allocate another array of length one:
int* dummy = new int[1];
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
Now you call your resize function:
TT * dummyThingy = &array[0];
dummyThingy
v
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
Now you delete the memory:
delete array;
dummyThingy
v
+-+-+-+-+-+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
(note: since you used the wrong type of delete it would not be suprising if only the first byte was deallocated. It just so happens that for primitive types my compiler will free the whole array if delete is called, but you should not rely on this.)
Next you allocate a new array:
array = new TT[newSize];
dummyThingy
v
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^ ^ ^ ^ ^
| --array--
dummy
array = dummyThingy;
dummyThingy
v
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now you leave your resizing function and dummyThingy goes out of scope
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now we return to main and allocate a new array.
int* otherArray = new int[2];
It just so happens that this will fit nicely at the start of our memory block.
otherArray
v
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now we write to otherArray[1]:
otherArray[1] = 9;
otherArray
v
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
| | | |7|9| | | | | | | | | | |
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
And magically the second element of the "array" array points to has changed. Because array doesn't point to an array. It points to random memory.
---------------------------------------------
That said, C++ does support dynamic arrays. Their names are std::vector (as BiTwhise already mentioned) and std::deque.
Enigma
[Edited by - Enigma on December 11, 2004 6:54:14 PM]
- operator new[] must be matched by a call to operator delete[], not operator delete as you have (note the square brackets).
- You leak memory. You declare a new array and assign it to array and then assign a new value to array, loosing the handle to the memory you just allocated.
- You return a dangling pointer. You leave array pointing to the memory you deallocated. Bad things can and will happen! For a start, that memory is likely to be reused elsewhere. Secondly attempting to delete that memory a second time is likely to cause your program to crash.
As an example, here's what happened when I tried to use your code:
#include <iostream>template <typename TT>//pass the adress of the first element of the array and the new size as paramsvoid increaseArrayDepth(TT * array, unsigned int newSize) //newSize should be bigger as the old size{ TT * dummyThingy = &array[0]; //store the pointer to the first element delete array; //delete the old array array = new TT[newSize]; //create a new one with the fitting size array = dummyThingy;};int main(){ int* array = new int[2]; array[0] = 7; array[1] = 7; int* dummy = new int[1]; increaseArrayDepth(array, 5); int* otherArray = new int[2]; otherArray[1] = 9; std::cout << array[1] << '\n';}
I think you'll agree that should print 7 right? It doesn't. It prints 9.
---------------------------------------------
EDIT: Just to make it clear what's happening. Image you have memory like this:
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
where each box is a byte.
You start your program and execute:
int* array = new int[2];
This allocates an array of two element from memory, i.e.:
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | |
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
^ ^
array
(the *'s indicate that the memory has been allocated)
Next you write the value 7 to these two locations:
array[0] = 7;
array[1] = 7;
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+-+-+-+-+-+-+-+-+-+-+
^ ^
array
Now you allocate another array of length one:
int* dummy = new int[1];
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
Now you call your resize function:
TT * dummyThingy = &array[0];
dummyThingy
v
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
Now you delete the memory:
delete array;
dummyThingy
v
+-+-+-+-+-+*+-+-+-+-+-+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+-+-+-+-+-+-+-+-+-+
^ ^ ^
array |
dummy
(note: since you used the wrong type of delete it would not be suprising if only the first byte was deallocated. It just so happens that for primitive types my compiler will free the whole array if delete is called, but you should not rely on this.)
Next you allocate a new array:
array = new TT[newSize];
dummyThingy
v
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^ ^ ^ ^ ^
| --array--
dummy
array = dummyThingy;
dummyThingy
v
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now you leave your resizing function and dummyThingy goes out of scope
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+-+-+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now we return to main and allocate a new array.
int* otherArray = new int[2];
It just so happens that this will fit nicely at the start of our memory block.
otherArray
v
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
| | | |7|7| | | | | | | | | | |
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
Now we write to otherArray[1]:
otherArray[1] = 9;
otherArray
v
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
| | | |7|9| | | | | | | | | | |
+-+-+-+*+*+*+*+*+*+*+*+-+-+-+-+
^ ^
array |
dummy
And magically the second element of the "array" array points to has changed. Because array doesn't point to an array. It points to random memory.
---------------------------------------------
That said, C++ does support dynamic arrays. Their names are std::vector (as BiTwhise already mentioned) and std::deque.
Enigma
[Edited by - Enigma on December 11, 2004 6:54:14 PM]
I'll agree here.
And I'm going to nip two common counter-arguments at the bud before they even get presented:
1. They're too slow: No, they're not. If the extra method calls on a vector are bottlenecking your program, by all means, do it your way. Know this though, a std::vector is implemented as an array that grows dynamically when you need it, just like you've got but you don't have to think about it. You can still index them like arrays if you want and you have access to a bunch of methods to do other things with them.
2. I don't like using other library code: STL is part of C++, so it's not really another library. Use it. That's what it's there for.
Further, that code you provided shouldn't work. You're pointing to your memory then deleting it. It's only a matter of time before that will segfault on you (and your pointer usage is a bit screwy too).
Try something like this:
EDIT: Fixed the problem Brother Bob had mentioned. I think sizeof will give me the size of the array and not the size of the pointer...
If it doesn't, include another parameter to indicate the old size.
EDIT++: Apparently sizeof won't help here (thanks Enigma), so the function now takes both sizes, which means you would have to know the previous size first... makes using std::vector sound more appealing now, doesn't it?
-Auron
[Edited by - Auron on December 11, 2004 6:56:47 PM]
And I'm going to nip two common counter-arguments at the bud before they even get presented:
1. They're too slow: No, they're not. If the extra method calls on a vector are bottlenecking your program, by all means, do it your way. Know this though, a std::vector is implemented as an array that grows dynamically when you need it, just like you've got but you don't have to think about it. You can still index them like arrays if you want and you have access to a bunch of methods to do other things with them.
2. I don't like using other library code: STL is part of C++, so it's not really another library. Use it. That's what it's there for.
Further, that code you provided shouldn't work. You're pointing to your memory then deleting it. It's only a matter of time before that will segfault on you (and your pointer usage is a bit screwy too).
Try something like this:
template <typename TT>// Note the double pointer here. You need that to be able to modify the array pointer.void increaseArrayDepth (TT **array, unsigned int newSize, unsigned int oldSize) { TT *newArray = new TT[newSize]; // You have to copy the array. Copying the pointer is not good enough here for (int i = 0; i < min(newSize, oldSize); i++) newArray = *array; // Assuming array was a new'ed array in the first place delete[] (*array); // You need to do this for the value to actually change outside the function *array = newArray;}
EDIT: Fixed the problem Brother Bob had mentioned. I think sizeof will give me the size of the array and not the size of the pointer...
If it doesn't, include another parameter to indicate the old size.
EDIT++: Apparently sizeof won't help here (thanks Enigma), so the function now takes both sizes, which means you would have to know the previous size first... makes using std::vector sound more appealing now, doesn't it?
-Auron
[Edited by - Auron on December 11, 2004 6:56:47 PM]
Quote: Original post by Auron... // You have to copy the array. Copying the pointer is not good enough here for (int i = 0; i < newSize; i++) newArray = *array; ...
You should not copy newSize elements. You should copy min(oldSize, newSize) elements. Anything else will overrun one of the arrays. However, since you don't know oldSize (in it's current form), you can't reliably copy the old content into the new array correctly.
Ahh right. Good call Brother Bob. I forgot that he was intending this to be able to downsize as well. I'm more used to the situation of dynamically up-sizing arrays. I'll make the edit above.
-Auron
-Auron
Quote: Original post by Auron
*snip*
EDIT: Fixed the problem Brother Bob had mentioned. I think sizeof will give me the size of the array and not the size of the pointer...
If it doesn't, include another parameter to indicate the old size.
-Auron
It won't. sizeof will only correctly report sizes of stack allocated arrays. For dynamically allocated arrays it will just return the size of the pointer (since that's all you pass it - a pointer).
Enigma
Quote: Original post by Auron
Ahh right. Good call Brother Bob. I forgot that he was intending this to be able to downsize as well. I'm more used to the situation of dynamically up-sizing arrays. I'll make the edit above.
-Auron
Even if you restrict the usage to upsizing only, you still have the problem with buffer overruns. You will read past the end of the old array, which might cause your application to crach with an access violation.
Of course, if you really wanted to write a function to resize raw arrays (you don't, you want to use std::vector or std::deque), it would probably be best written like:
Enigma
template <typename T>// Note the reference to pointer here. Same function as a double pointer but nicer syntax for the caller.void increaseArrayDepth(T*& array, unsigned int newSize, unsigned int oldSize){ T* newArray = new T[newSize]; try { std::copy(array, array + std::min(oldSize, newSize), newArray); } catch (...) { // avoid leaking newArray if copy throws an exception delete[] newArray; throw; } delete[] array; array = newArray;}
Enigma
Quote: Original post by Brother BobQuote: Original post by Auron
Ahh right. Good call Brother Bob. I forgot that he was intending this to be able to downsize as well. I'm more used to the situation of dynamically up-sizing arrays. I'll make the edit above.
-Auron
Even if you restrict the usage to upsizing only, you still have the problem with buffer overruns. You will read past the end of the old array, which might cause your application to crach with an access violation.
Ugh, yes, you're right. Man, did I botch that. Bad day I guess. Thanks for alerting me to my wrongness again.
-Auron
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement