I was thinking of ways to make an array larger quickly in C++ and I came up with this:
// set up arr1
int *arr1 = new int[5];
// add data to arr1
arr1[0] = 1;
arr1[1] = 2;
arr1[2] = 3;
arr1[3] = 4;
arr1[4] = 5;
// set up arr2
int *arr2 = new int[10];
arr2 = arr1; // assign arr1 to arr2
// add more values
arr2[5] = 6;
arr2[6] = 7;
arr2[7] = 8;
arr2[8] = 9;
arr2[9] = 10;
Is this even safe? I worry that this will cause some strange behavior and that arr2 is just an int[5] array and you're now overwriting data that doesn't belong to it.
NO.
arr2 = arr1;
Does not do what you think it does!
There is already something in the STL for this, it's called vector.
For the sake of completeness.. :)
arr2 before the assignment held the address if the start of the array of 10 items you allocated. After the assignment, rather than copying the contents the block addressed by arr1 (which I guess is what you wanted to do), the assignment merely changes the address that arr2 holds to the address that arr1 holds (i.e. start of the 5 item array), for the rest of your code, this has two consequences:
You no longer have the address of the block of 10 items you allocated, so you have no way of releasing that block - hence you have a memory leak (should your program continue to operate)
Though you initially allocated 10 items for arr2, by the assignment, you're now addressing a block that only has 5 items, and accessing anything outside of that block of 5 (i.e. indexes 5 onwards) is likely to end in nasal daemons paying a visit with a very large cricket bat - or you may get lucky...
So what can you do:
Use std::vector<int>, one if it's design features is to take this kind of burden away
from you (unless you're interested in implementing another container), if that's the case
Use a copy operation to correctly copy the contents rather than assign the addresses, as suggested, use std::copy - or others, such as memcpy (prefer memmove - it's a little slower but is well defined for overlapping blocks etc.)
The line arr2 = arr1; leaks memory, and all the following arr2[...]= lines invoke undefined behavior as they access the array of 5 ints outside of its bounds.
To do what you wanted to do, replace arr2 = arr1; with std::copy(arr1, arr1+5, arr2); (example program: https://ideone.com/3Rohu)
To do this properly, use std::vector<int>
This leaks memory, because you are assigning pointers.
int *arr2 = new int[10];
arr2 = arr1; // this does not assign the values
The second assignment makes arr2 point to the array arr1. You loose the array you have previously allocated, and can never delete it.
To avoid this, use an std::vector.
No, it's not safe at all. When you said arr2=arr1; you didn't copy any of the data across, you just reassigned a pointer. Now the memory you allocated with int *arr2 = new int[10]; has been leaked, and arr2 points to the original 5-element array, and when you start saying things like arr2[5] = 6; you are writing beyond the end of that array and all hell may break loose.
It's not safe that's actually a memory leak. You're not doing anything with arr2 you just ended up setting arr2 to the address of r1, remember in c and c++ theres no bounds checking so you're overwriting another stackframe by doing that
Is this even safe?
No
arr2 = arr1; // assign arr1 to arr2
arr2 is also pointing to first index element of arr1. And by doing it, arr2 looses the location where it was earlier pointing to returned by new. So, there is memory leak.
Use std::vector instead to expand the array.
Just to add to the other comments which are correct. The reason why you will leak memory is that you have replaced arr2 pointer that pointed to allocated memory for arr2 with arr1 pointer. Now arr1, arr2 point to the same thing, the array of 5 elements. When you try to clean up with delete operator, you can only clean up the 5 element array since the pointer to 10 element array has been overriden.
Related
This question already has answers here:
How do I use arrays in C++?
(5 answers)
Closed 6 years ago.
I have an array of integers that I pass into function. Then I want to make a dynamic array in that function as a copy of the first array, but whenever I change the value of an element in second array (copy), the value in the first array also changes. What am I doing wrong?
example:
int *array1 = new int[N]
int *array2 = new int[N]
array2 = array1;
array2[1]=2; //element of the first array at index 1 also becomes 2
Thank you.
The Explanation:
Since we are dealing with C++, there is pretty much no reason to use new int[N] instead of std::vecotr<int> of a size N.
To explain what is happening in your code: You create a pointer named array1 and allocate memory enough to contain N integers. You do the same with second pointer, called array2. Since the names array1 and array2 are just pointers to memory, what they point to can be changed. In the line array2 = array1 you change to what is pointing array2 to memory allocated and pointed to by array1 pointer.
So what happens now? Really bad stuff. You are encountering a memory leak. The memory that was pointed to by array2 is still there, still allocated, but you are unable to access it, since your only access to it (array2 pointer) now points to entirely different part of memory. You now also have 2 pointers that point to the same memory (originally the memory that was allocated in the line int *array1 = new int[N]).
How to improve your code?
Use std::vector<int>. Vector class comes with well written and safe assignment operator that will work for you here:
std::vector<int> array1(N);
std::vector<int> array2(N);
array2 = array1;
Now you have 2 identical vectors, memory is managed well (array1 and array2 are separate entities. They do not share the same memory and can be freely changed without affecting the other one) and your code looks pretty.
What if you cannot change everything to std::vector?
You mentioned having an array that you pass into a function. Let's call it an original_array of a size N. Consider this code, which uses similar signature, but uses safe memory management by converting array to vector:
void copy_and_do_stuff(int original_array[], int N)
{
std::vector<int> array2;
std::copy(original_array, original_array + N, array2.begin());
// here, the vector "array2" is a copy of your `original_array`. Changes
// to it will not affect your argument.
// ... do whatever you need to do in this function ...
}
Remember to add #include <vector> and #include <algorithm> to use vectors and std::copy function.
To give a full answer, although the comments say everything important so far:
The line array2 = array1; does the following: take the address that is stored in array1 and store it also in array2. They are both pointing at the same location now (and the old, reserved storage is still reserved but not pointed at, aka a memory loss)
In any case, pointers are somewhat dangerous things, easily leading to unexpected behaviour, especially if you are a beginner. Therefore, you want to use std::vector:
//at the head of the file
#include <vector>
using std::vector;
//in the program
vector<int> array_1(n);
//do something with array_1:
array_1[0] = 1;
array_1[1] = ...
vector<int> array_2 = array_1; //actually copies the content
or:
vector<int> array_2(array_1); //copy constructor
At some point you will want to investigate how vector works internally (it wraps an array, actually), but for now, simply use vector. Read it's documentation. You can do all sorts of things on it with the STD library, like for example having it sorted by std::sort.
As mentioned in the comments you probably shouldn't be doing this in c++; std::vector is THE optimized; safe and reliable way to handle data of this sort.
But it is clear from your question that you don't really understand pointers.
This is what you code does:
int *array1 = new int[N]; //allocate memory for the first array
int *array2 = new int[N]; //allocate memory for the second array
array2 = array1; //OVERWRITE the location of the second array
//with that of the first (thus destroying your
//only pointer to the second array and creating a
//memory leak)
To achieve what you want in array2=array1 you need to write a loop and copy every integer ELEMENT of the array across to the new array - this creates a 'deep' copy.
array1 and 2 are pointers in your case.
When you do:
array2 = array1;
array2 points to the same memory location as array1, and the old array2 is lost in memory (and so it creates a memory leak, as Elemental said)
You need to copy the array elements by hand or use std::vector
My C++ algorithm obtains data with unknown size (it detects particles on the image one by one, and I cannot know how many particles will be detected before this algorithm finishes its work).
So, first I want to allocate, say, array with 10000 elements, and during the processing, if necessary, allocate another 10000 elements several times.
Here is what I tried, it doesn't work:
#include <iostream>
using namespace std;
int main(){
int n = 3;
int m = 3;
float *a = new float[3];
a[0] = 0;
a[1] = 1;
a[2] = 2;
float *b = a + 2;
b = new float[3];
b[0] = 4;
b[1] = 5;
cout << a[3] << endl;
}
As a result, I got minus infinity. Of course, I can handle this in different arrays, I can allocate a huge amount of memory once. I need to pass full array of detected data to the function after, so, as a result, I want to have one big array.
But still, is there a way to increase the size of your dynamically allocated way? What I want in a toy example is to increase number of elements in array a by 3, so it will have 6 elements.
In Matlab it is absolutely possible. What about C++?
Thanks
You should just use std::vector instead of raw arrays. It is implemented to grow efficiently. You can change its size with resize, append to it with push_back or insert a range (or various other things) with insert to grow it.
Changing the size of a manually allocated array is not possible in C++. Using std::vector over raw arrays is a good idea in general, even if the size does not change. Some arguments are the automated, leak-proof memory management, the additional exception safety as well as the vector knowing its own size.
No, you can't increase the size of an array. If you want to use an array, you'll have to allocate a new block, large enough for the whole new array, and copy the existing elements across before deleting the old array. Or you could use a more complicated data structure that doesn't store its elements contiguously.
Luckily, the standard library has containers to handle this automatically; including vector, a resizable array.
std::vector<float> a(3);
a[0] = 0;
a[1] = 1;
a[2] = 2;
// you can resize it by specifying a new size
a.resize(4);
a[3] = 3;
// or by appending new elements
a.push_back(4);
You should use a vector and then resize when necessary or let it grow by itself.
When you do:
float *b = a + 2;
b = new float[3];
the memory allocated will not be allocated contiguously to the first allocation even if you previously set the pointer to point at the end(it will be overwritten anyway). Therefore, when accessing a[3], you get out of bound.
Title says it all more or less. When I need an (for the sake of this example) integer array for an unknown amount of values I know I can change it's size using new *array = new int[size]. Now my question is: If I have an array of a certain size, but I need to make it bigger, can I just use the new operator to expand it and will it still have all previously stored elements or would it be smarter to create a whole new array with a dynamic size, copy all elements from the previous array into the new one and delete[] the old array. Basically just swapping between the two arrays, whenever I need a new size.
Specifically I am asking whether or not this piece of code would work in the way it's intended to work
for(int i = 1; i < 10; i++){
int *array = new int[i];
array[i-1] = i;
}
My assumption is that this array will first be the size of 1 and store the value 1 at index 0. Then it will reallocate its size to 2 and store the value to at index 1 and so on until i is 9.
I guess to rephrase my question a bit better: Does an array initialized with new have to be populated with elements or will it copy the elements it had from before using the operator?
You can't resize the array in this way. You need to make a new array and then copy the old array into it. You can also try std::vector, which does what you want automatically.
If you want to use pointers rather than std::vector to change the size of your array, you can do it in this way.
int n = 100; // This will be the number of elements.
int *array1; // Pointer
array1 = new int[n]; // This will allocate your array with size n, so you will have 100 elements. You can combine this with the previous in int *array1 = new int[n];
So fill up the this array however you please...
Then you decide you want a 200 element array instead? You will need to create a different array in the same way.
int *array2 = new int[200];
You can use the for loop to copy array 1 into array 2. The for loop should iterate as many times as there are elements in array 1 (100).
for(int i = 0; i < 100; ++i)
array2[i] = array[1];
At this stage array2 is exactly the same as array1, but with 100 uninitialized elements at your disposal from [100] to [199].
You won't need array1 anymore, so at some point, you should call
delete [] array1;
Your assumption, by the way would not work, because on the first cycle of your loop, you create (or try to create) an array of i=1 element. Arrays start counting at 0, so your only single element is [0]. When i is at 0, what is i-1?
If you try to access array[-1], you'll probably crash. But why should you want to create 10 different arrays? new keyword creates an unrelated object, not overwrites the one with the same name.
Does an array initialized with new have to be populated with elements or will it copy the elements it had from before using the operator?
new[] allocates new array, completely independent from previous.
I know 3 ways to "make the array bigger":
As #ravi mentioned, don't mess with poinsters, use modern std::vector.
Make new array in new pointer, std::move elements from old array to the new one, and then delete[] old array.
Get rid of new[] & delete[], use old realloc with malloc & free.
You have to allocate new array and copy old array's data into that. This is how vector is implemented. Had there been better way of doing it, C++ standard community would have considered that.
int* array1 = new[ 10 ]( );
What is the correct way to copy this array? The following is my attempt.
int* array2 = array1;
With that said, is the following the correct way to add a value to a full array1?
int val = 2;
int* array2 = new[ 11 ]( );
for ( int i = 0; i < 10; i++ ) {
array2[ i ] = array1[ i ];
}
array2[ 10 ] = val;
delete [ ]array1;
One more question, how do I get the amount of spaces that are filled in an array?
int* array1 = new[ 2 ];
array1[ 0 ] = 1;
// the resulting array would look like this { 1 };
I want to get the number of initialized elements, so instead of returning 2 for the number of elements the allocated array can hold, I want to get how many elements actually have a value. I want to do this so I can replace the 10 in my previous code with the size of array1.
Thank you!
EDIT: I need to stick with c style arrays because of my instructor.
int* array2 = array1;
assigns one pointer to another, i.e. it copies an address stored in array1 and makes array2 to point to this address as well.
If you want to make a deep copy while still using pointers you could use memcpy:
int* array2 = new int[10];
memcpy(array2, array1, 10 * sizeof(int));
which is ghastly C-style approach that you should avoid always when it is possible.
The only reasonable solution that I can think of is using std::array or std::vector instead of
C-style arrays. std::vector unlike an array is an object that has its own assignment operator (as well as copy constructor) that copies it for you the way you would expect it:
std::vector<int> vec1(10, 0); // 10 numbers initialized to 0
std::vector<int> vec2 = vec1; // note this actually calls copy constructor
std::vector<int> vec3(vec1); // the same as previous line
vec3 = vec1; // assignment that makes a deep copy for you
You can start with an empty std::vector and keep pushing new elements to it using its push_back method and retrieve its size at any time calling vec.size(). Additionally: you are not responsible for cleaning up the memory where its elements were stored - if you use it as I pointed out in the above code, these are objects with automatic storage duration that clean up the memory for you when they go out of scope.
Your first attempt doesn't copy the contents of the array, it simply creates a new pointer that points to the beginning of the array.
Your method of adding a value looks correct.
The array itself does not remember which members you have assigned values to. If you want to keep track of how many elements are "filled", you'll have to do that yourself. You could wrap the array in, say, a template class, and perhaps call it myVector.
Lets say, i have
int *p;
p = new int[5];
for(int i=0;i<5;i++)
*(p+i)=i;
Now I want to add a 6th element to the array. How do I do it?
You have to reallocate the array and copy the data:
int *p;
p = new int[5];
for(int i=0;i<5;i++)
*(p+i)=i;
// realloc
int* temp = new int[6];
std::copy(p, p + 5, temp); // Suggested by comments from Nick and Bojan
delete [] p;
p = temp;
You cannot. You must use a dynamic container, such as an STL vector, for this. Or else you can make another array that is larger, and then copy the data from your first array into it.
The reason is that an array represents a contiguous region in memory. For your example above, let us say that p points to address 0x1000, and the the five ints correspond to twenty bytes, so the array ends at the boundary of 0x1014. The compiler is free to place other variables in the memory starting at 0x1014; for example, int i might occupy 0x1014..0x1018. If you then extended the array so that it occupied four more bytes, what would happen?
If you allocate the initial buffer using malloc you can use realloc to resize the buffer. You shouldn't use realloc to resize a new-ed buffer.
int * array = (int*)malloc(sizeof(int) * arrayLength);
array = (int*)realloc(array, sizeof(int) * newLength);
However, this is a C-ish way to do things. You should consider using vector.
Why don't you look in the sources how vector does that? You can see the implementation of this mechanism right in the folder your C++ include files reside!
Here's what it does on gcc 4.3.2:
Allocate a new contiguous chunk of memory with use of the vector's allocator (you remember that vector is vector<Type, Allocator = new_allocator>?). The default allocator calls operator new() (not just new!) to allocate this chunk, letting himself thereby not to mess with new[]/delete[] stuff;
Copy the contents of the existing array to the newly allocated one;
Dispose previously aligned chunk with the allocator; the default one uses operator delete().
(Note, that if you're going to write your own vector, your size should increase "M times", not "by fixed amount". This will let you achieve amortized constant time. For example, if, upon each excession of the size limit, your vector grows twice, each element will be copied on average once.)
Same as others are saying, but if you're resizing the array often, one strategy is to resize the array each time by doubling the size. There's an expense to constantly creating new and destroying old, so the doubling theory tries to mitigate this problem by ensuring that there's sufficient room for future elements as well.