C++: Remove element from dynamic struct array and shift other elements - c++

I have an array of structs. I am trying to delete a list of elements from that array and shift other elements to the left. After shifting the elements I am trying to delete/free the memory at the end of the array which we don't require anymore. I have the following code:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
void removeelement(int*);
void displayelements();
typedef struct {
int n;
}element;
element** array;
int numofelements=5;
int main() {
array = (element**)malloc(5*sizeof(element*));
for(int i=0;i<5;i++){
array[i] = new element;
array[i]->n=i;
}
int removelist[3] = {1,3,4};
removeelement(removelist);
displayelements();
return 0;
}
void removeelement(int* removelist){
for(int i=0;i<3;i++){
int index = removelist[i];
int j;
for(j=index;j<numofelements-2;j++){
array[j] = array[j+1];
}
delete [] array[j+1];
numofelements--;
}
}
void displayelements(){
int i=0;
while(i<numofelements){
printf("%d\n",array[i]->n);
i++;
}
}
But delete [] array[j+1]; is causing an exception:
*** Error in `main': double free or corruption (fasttop): 0x0000000001861cb0 ***
I don't understand what's causing this. As many people have suggested in other forums, I am using the 'new' operator to create a new,dynamic element.
EDIT:
I made the following changes:
I changed for(j=index;j<numofelements-2;j++){ to for(j=index;j<numofelements-1;j++){
int index = removelist[i] to int index = removelist[i]-i
I removed delete [] array[j+1] put delete array[numofelements+1] outside both the for loops.
Though I had used delete only on one element, It dealloced memory for the other redundant elements as well, which is interesting.
This is the final code:
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
void removeelement(int*);
void displayelements();
typedef struct {
int n;
}element;
element** array;
int numofelements=5;
int main() {
array = (element**)malloc(5*sizeof(element*));
for(int i=0;i<5;i++){
array[i] = new element;
array[i]->n=i;
}
int removelist[3] = {1,3,4};
removeelement(removelist);
displayelements();
return 0;
}
void removeelement(int* removelist){
for(int i=0;i<3;i++){
int index = removelist[i]-i;
int j=index;
for(;j<numofelements-1;j++){
array[j] = array[j+1];
}
numofelements--;
}
delete array[numofelements+1];
}
void displayelements(){
int i=0;
while(i<5){
printf("%d\n",array[i]->n);
i++;
}
}
I got it working using this code. But I am going to use std::vector as many of you suggested.

You used delete[] expression on a pointer that was not returned by new[] expression. Therefore the behaviour of the program is undefined.
Anything that was allocated with new must be deallocated with delete. delete[] will not do.
Even if you had used the correct expression, there's another bug:
int numofelements=5;
//...
for(int i=0;i<3;i++){
int index = removelist[i];
int j;
for(j=index;j<numofelements-2;j++){
array[j] = array[j+1];
}
delete [] array[j+1];
numofelements--;
}
After the first iteration of the outer loop, array[4] has been deleted. Note that since removelist[i] == 1, I suspect that array[4] wasn't supposed to be deleted in the first place.
During the second iteration, array[4] will be deleted again. Since this points to the already deleted object, the behaviour is undefined.
Furthermore, copies of the deleted pointer remain in the array due array[j] = array[j+1] in the inner loop, while some of the pointers will be overwritten and therefore their memory will be leaked. Simple fix to your algorithm: Delete the pointer at index first, and shift elements after deletion.
Even more: If your loop worked as you intended, first 2 iterations would have each removed an element of the array, thus reducing numofelements to 3. Then, you'd be removing an element at index 4 of an array that has valid pointers in indices 0..2. Presumably the indices to be removed must be sorted; In that case, this can be fixed by deleting the index removelist[i] - i to acount for the shifts. Another clever strategy is to remove the indices from high to low, as suggested by Paul.
Other things to consider:
The program leaks the memory allocated for array. That might not be a problem to this trivial program, but it would be a good idea to have a habit of deallocating all memory that was allocated lest you forget to do so when it matters.
It's a bad idea to use malloc unless one has specific and reasonable justification to do so. One usually doesn't have a reasonable justification to use malloc.
It's a bad idea to allocate dynamic memory at all without using a RAII container. The bugs in this program would have been trivially been avoided if std::vector had been used.

Apart from the obvious errors in memory management, the approach in general could have been made simpler if you first sorted the removelist array, and then work backwards in that array starting from the last entry going toward the first entry.
Doing this would have changed the way the array was being resized in that you would have been doing the resizing (shifting elements) on entries that will no longer be affected. In your current code, you are shifting entries, and in subsequent iterations of the loop, you need to revisit those shifted entries with a now "invalid" removelist set of indices to remove.
See mayaknife's and user2079303 answers to illustrate the issue of invalid entries after removing each item (going from lowest entry to highest entry in the removelist array). As pointed out, even a usage of std::vector would not have helped you, since this issue points out the flaw in the basic logic being used to remove the elements.
Here is how you might have addressed this in your current code if you were to work going backwards in the removelist array ( I say "might have addressed", since this is not fully tested, but it illustrates more or less the point being made):
void removeelement(int* removelist)
{
for(int i = 2; i >= 0 ; --i)
{
int index = removelist[i];
array* elementToDelete = array[index];
for(j=index; j < numofelements -2; j++)
{
array[j] = array[j+1];
}
delete [] elementToDelete;
numofelements--;
}
}
Thus on each iteration, the removelist index will still be valid, since you're going from highest entry to lowest entry in the entries to delete. Work this out on paper and you see if you reversed the way you iterated through the removelist array, you should see how this works, as opposed to going forward through the removelist array.
You also have other issues with the code, such as mixing malloc with delete[]. Doing so is undefined behavior -- never mix allocation / deallocation methods like this in a C++ program.
Having said this, here is another version of your program, but not using manual memory management:
#include <vector>
#include <algorithm>
#include <iostream>
#include <array>
struct element {
int n;
};
int main()
{
std::vector<element> arr(5);
for (int i = 0; i < 5; ++i)
arr[i].n = i;
std::array<int, 3> removelist = {1,3,4};
// sort the list
std::sort(removelist.begin(), removelist.end());
// work backwards, erasing each element
std::for_each(removelist.rbegin(), removelist.rend(),[&](int n){arr.erase(arr.begin() + n);});
// output results
for( auto& v : arr)
std::cout << v.n << '\n';
}
Live Example
Note the usage of the reverse iterators rbegin() and rend(), thus mimicking the backwards traversal of the removelist container.

This line:
delete [] array[j+1];
deletes the array of elements pointed to by 'array[j+1]'. But 'array[j+1]' was initialized by this line:
array[i] = new element;
which only allocates a single element, not an array of elements, so the deletion should only delete a single element as well. E.g:
delete array[j+1];
The main problem, however, is that the wrong elements are being deleted. To see why, let's assume that the loop which initializes 'array' assigns it pointers to five 'element' structures which we will refer to as A, B, C, D and E.
Before the call to removeelements(), 'array' contains the following pointers:
array[0] -> A
array[1] -> B
array[2] -> C
array[3] -> D
array[4] -> E
'numofelements' is 5.
Inside removeelements(), the first element to be removed is 1 and the inner loop looks like this:
for(j=1;j<3;j++){
array[j] = array[j+1];
}
This will result in the contents of 'array[2]' being copied into 'array[1]' and 'array[3]' being copied into 'array[2]. After that 'array' contains the following:
array[0] -> A
array[1] -> C
array[2] -> D
array[3] -> D
array[4] -> E
At this point 'j' contains 3 so 'delete array[j+1]' will delete the element pointed to by 'array[4]', which is 'E'.
'numofelements' is then decremented to 4.
The second element to be removed is 3. Because 'numofelements' is now 4, the inner loop will look like this:
for(j=3;j<2;j++){
array[j] = array[j+1];
}
'j' will be initialized to 3. That is greater than 2 so the body of the loop won't execute and 'array' will be left unchanged.
Since 'j' is 3 'delete array[j+1]' will again delete 'array[4]', which still points to E. So E is deleted a second time, resulting in the error that you are getting.
Were the program to continue, 'numofelements' would be decremented to 3 and we'd move on to the third element to be removed, which would be 4. This would give an inner loop like this:
for(j=4;j<1;j++){
array[j] = array[j+1];
}
'j' would be initialized to 4 and once again the body of the loop would not be executed. 'delete array[j+1]' would attempt to delete the element pointed to by 'array[5]', which is beyond the bounds of 'array' and would result in an exception.
As others have suggested, the best way to handle this is to use std::vector. However, the way your code is structured even std::vector will fail to give you the results you want because as soon as you delete one element from 'array' the indices of all of those which follow it will change, meaning that the remaining indices in 'removelist' will no longer be correct.
I suggest that whatever changes you make, you manually step through the code, as I have above, tracking the contents of the array and relevant variables so you can understand exactly what your code is doing.

Related

Problem implementing a dynamically-sized array

I'm writing an implementation of a dynamically-sized array. The code compiles without errors, but the array elements don't get copied properly. They seem to just get erased (overwritten with 0's). Trying to call a getter on an array element causes a segfault.
The array holds pointers to some basic class objects; this is the main difference between my code and the examples I looked up.
This is the function:
// Pointer to array of pointers
SomeClass** mainArray = new SomeClass[1];
int numItems = 0;
void AddItemDynamic(SomeClass* newVal) {
SomeClass** tempArray = new SomeClass*[numItems+1];
// Copying pointers to bigger array
for (int i = 0; i < numItems - 1; i++) {
tempArray[i] = mainArray[i];
}
numItems++;
// Adding the new value
tempArray[numItems] = newVal;
delete [] mainArray;
mainArray = tempArray;
}
The code should copy the array elements over, then reassign the pointer to the newly created array. Instead, the pointer seems to be set to something else.
If the current array have numItems element in them, then the loop
for (int i = 0; i < numItems - 1; i++)
will copy one less than numItems elements.
And when you add the new element, you go out of bounds of the new array, because you increase numItems to early.
So two off-by-one errors in the same function, one in each direction.
And as mentioned in a comment (thanks Ayxan) the first off-by-one error will mean that the first two times you call this function, the copying loop won't happen. That's actually good when doing it the first time as then there's nothing to copy, but the second time there should be something to copy and yet the loop (currently) won't run.

Copying values from one vector to another (from book)

Consider this piece of code.
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector <int *> test;
vector <int *> v;
int *a = new int;
int *b = new int;
*a = 1;
*b = 2;
v.push_back (a);
v.push_back (b);
for (int i = 0; i < 2; ++i)
{
int n = *v[i];
test.push_back (&n);
}
cout << *test[0] << " " << *test[1] << endl;
delete a;
delete b;
return 0;
}
The problem's statement is:
"Given this code, answer the following questions:
Why does "test" vector contain only 2's?
How can we change for loop to copy properly (only code inside for loop)?"
I couldn't answer any of these questions, so a little bit of help will be appreciated.
Thanks in advance.
That code introduces dangling pointers. The body of the loop looks like this:
{
int n = *v[i];
test.push_back (&n);
}
The local variable n loses scope as soon as the loop body ends, so the pointer &n is now a dangling pointer. If it happens that test contains only 2's, that's just what randomly came out of what is undefined behavior.
If you want to "properly" copy the data over to test, you can change the for loop body to this:
{
int* n = new int;
*n = *v[i];
test.push_back (n);
}
Please take the "properly" with a grain of salt...
You push two the same pointers to n into test array. n equals the last element of your first array. Note that after control flow exited the loop, all pointers to n become invalid. So, in fact your test array contains invalid pointers, not pointers to 2s.
You should create a copy of each integer:
int* n = new int(*v[i]);
test.push_back (n);
Note also that you have memory leak here. Each int created using new should be later destroyed using delete.
The first question is a trick question: The vector contains pointers to a variable that no longer exists, and dereferencing that could cause pretty much any output. I imagine on some machines and compilers it prints all 2s however.
I can't understand what the exercise is trying to do (why does it use vectors of pointers for example) so I can't really help with how to solve the problem.
One way you could do it is by making test store by value:
First change the test vector to vector <int> test;
Then change the push_back to something like test.push_back (n); and finally the print statements to remove the now-unneeded * operators.
EDIT for comment:
First, I'm suspect of this book: It shouldn't be demonstrating undefined behavior or raw pointers to single builtin types. But you can change your loop body if you want:
for (int i = 0; i < 2; ++i)
{
int* n = new int;
*n = *v[i];
test.push_back (&n);
}
Note that both this will cause a memory leak unless you later delete those pointers, a problem that storing by value eliminates.
1) I think that the premise of the question is faulty. The loop adds two elements to test, each contains the address of the automatic variable n, the scope of which is limited to the body of the loop. It's not guaranteed that n will be allocated the same memory location in both passes through the loop, but I suppose that it's likely that most compilers will reuse the same location in both passes.
Moreover, n is out of scope at the output statement. So referencing the pointers in test to those memory locations is undefined. Again, there's a good chance that they will still contain the values assigned in the loop.
So, only if the same location gets reused for n in the second pass of the loop and that location has not been overwritten at the time the output statement is executed, will the output be "2 2". There is no guarantee of either of these premises.
2) To get the output "1 2" without changing anything outside the loop, one could change the definition of n to int& n = *v[i], which would be a single character change from the given code, though the end result is rather strange.
A simpler solution would be to eliminate the temporary n and simply test.push_back(v[i]).

How to access the array from the pointer?

I am trying to understand how pointers work but I do not know how a pointer to only the first element can be used to access all the array
int myArray[10];
for(int i=0; i<10; i++)
{
myArray[i] = 11*i;
}
int *p;
p = myArray;
//Now how do I access the complete array using the variable p
cout<<*p; //This only prints the first value, how to print all the values
You have to use while or for.
int i = 0;
while (i < 10)
{
cout << p[i];
i += 1;
}
Pointers and arrays are working in the same way. An array is nothing else than a pointer to the first element you allocated.
If you for example want to access pos 5, you can just write:
...
int *p;
p = myArray;
cout << p[5];
Since the compiler know that p is a pointer to an int, it will add the size of an int for each step (4 bytes in this case).
As long as you don't use pointers to void, the compiler does this for you.
You still have to keep track of the length of the array so you do not exceeds it since a pointer don't do that.
except for thr declaration, arrays and pointers con be used using the same syntax
(They are different in memory, meaning they still need to be treated differently)
Use like this,
int *p;
p = myArray;
for(int i=0;i<10;i++)
{
cout<<*(p+i);
}
The first element points to the first memory location of the elements in the array. So this:
myArray[0];
and
myArray;
point to the same location. You can use indexes on the pointer, just like you did to fill the array. So this:
int *p = myArray;
cout << p[0];
cout << p[1];
would access your other elements. You can use a for loop to access all the elements in the array, just like you did to populate it in the first place.
You can think of the name of an array as a pointer to its first element.
So, the line p = myArray; is simply copying the address of the first element of the array myArray into p.
Now the line cout<<*p; is obviously displaying the value of what's pointed by p, which is the first element of your array.
To display all the elements, you can simply use a for loop like you did before.

Deleting an element from an array of pointers

I'm creating a custom vector class as part of a homework assignment. What I am currently trying to do is implement a function called erase, which will take an integer as an argument, decrease my array length by 1, remove the element at the position specified by the argument, and finally shift all the elements down to fill in the gap left by "erased" element.
What I am not completely understanding, due to my lack of experience with this language, is how you can delete a single element from an array of pointers.
Currently, I have the following implemented:
void myvector::erase(int i)
{
if(i != max_size)
{
for(int x = i; x < max_size; x++)
{
vec_array[x] = vec_array[x+1];
}
vec_size --;
//delete element from vector;
}
else
//delete element from vector
}
The class declaration and constructors look like this:
template <typename T>
class myvector
{
private:
T *vec_array;
int vec_size;
int max_size;
bool is_empty;
public:
myvector::myvector(int max_size_input)
{
max_size = max_size_input;
vec_array = new T[max_size];
vec_size = 0;
}
I have tried the following:
Using delete to try and delete an element
delete vec_size[max_size];
vec_size[max_size] = NULL;
Setting the value of the element to NULL or 0
vec_size[max_size] = NULL
or
vec_size[max_size] = 0
None of which are working for me due to either operator "=" being ambiguous or specified type not being able to be cast to void *.
I'm probably missing something simple, but I just can't seem to get passed this. Any help would be much appreciated. Again, sorry for the lack of experience if this is something silly.
If your custom vector class is supposed to work like std::vector, then don't concern yourself with object destruction. If you need to erase an element, you simply copy all elements following it by one position to the left:
void myvector::erase(int i)
{
for (int x = i + 1; x < vec_size; x++) {
vec_array[x - 1] = vec_array[x];
}
vec_size--;
}
That's all the basic work your erase() function has to do.
If the elements happen to be pointers, you shouldn't care; the user of your vector class is responsible for deleting those pointers if that's needed. You cannot determine if they can actually be deleted (the pointers might point to automatic stack variables, which are not deletable.)
So, do not ever call delete on an element of your vector.
If your vector class has a clear() function, and you want to make sure the elements are destructed, simply:
delete[] vec_array;
vec_array = new T[max_size];
vec_size = 0;
And this is how std::vector works, actually. (Well, the basic logic of it; of course you can optimize a hell of a lot of stuff in a vector implementation.)
Since this is homework i wont give you a definitive solution, but here is one method of erasing a value:
loop through and find value specified in erase function
mark values position in the array
starting from that position, move all elements values to the previous element(overlapping 'erased' value)
for i starting at position, i less than size minus one, i plus plus
element equals next element
reduce size of vector by 1
see if this is a big enough hint.

Arrays and pointers in a template

I am attempting to write a template/class that has a few functions, but I'm running into what seems like a rather newbie problem. I have a simple insert function and a display values function, however whenever I attempt to display the value, I always receive what looks like a memory address(but I have no idea), but I would like to receive the value stored (in this particular example, the int 2). I'm not sure how to dereference that to a value, or if I'm just completely messing up. I know that vectors are a better alternative, however I need to use an array in this implementation - and honestly I would like to gain a more thorough understanding of the code and what's going on. Any help as to how to accomplish this task would be greatly appreciated.
Example Output (running the program in the same way every time):
003358C0
001A58C0
007158C0
Code:
#include <iostream>
using namespace std;
template <typename Comparable>
class Collection
{
public: Collection() {
currentSize = 0;
count = 0;
}
Comparable * values;
int currentSize; // internal counter for the number of elements stored
void insert(Comparable value) {
currentSize++;
// temparray below is used as a way to increase the size of the
// values array each time the insert function is called
Comparable * temparray = new Comparable[currentSize];
memcpy(temparray,values,sizeof values);
// Not sure if the commented section below is necessary,
// but either way it doesn't run the way I intended
temparray[currentSize/* * (sizeof Comparable) */] = value;
values = temparray;
}
void displayValues() {
for (int i = 0; i < currentSize; i++) {
cout << values[i] << endl;
}
}
};
int main()
{
Collection<int> test;
int inserter = 2;
test.insert(inserter);
test.displayValues();
cin.get();
return 0;
}
Well, if you insist, you can write and debug your own limited version of std::vector.
First, don't memcpy from an uninitialized pointer. Set values to new Comparable[0] in the constructor.
Second, memcpy the right number of bytes: (currentSize-1)*sizeof(Comparable).
Third, don't memcpy at all. That assumes that Comparable types can all be copied byte-by-byte, which is a severe limitation in C++. Instead:
EDIT: changed uninitialized_copy to copy:
std::copy(values, values + currentSize - 1, temparray);
Fourth, delete the old array when it's no longer in use:
delete [] values;
Fifth, unless the code is going to make very few insertions, expand the array by more than one. std::vector typically increases its size by a factor of 1.5.
Sixth, don't increment currentSize until the size changes. That will change all those currentSize-1s into currentSize, which is much less annoying. <g>
Seventh, an array of size N has indices from 0 to N-1, so the top element of the new array is at currentSize - 1, not currentSize.
Eighth, did I mention, you really should use std::vector.
This line is wrong:
memcpy(temparray,values,sizeof values);
The first time this line is run, the values pointer is uninitialized, so it will cause undefined behavior. Additionally, using sizeof values is wrong since that will always give the size of a pointer.
Another issue:
temparray[currentSize] = value;
This will also cause undefined bahavior because you have only allocated currentSize items in temparray, so you can only access indices 0 to currentSize-1.
There is also an error in your array access.
temparray[currentSize/* * (sizeof Comparable) */] = value;
Remember that arrays start at index zero. So for an array of length 1, you would set temparray[0] = value. Since you increment currentSize at the top of the insert function, you will need to do this instead:
temparray[currentSize-1] = value;