I was trying to use vectors in C++. I am trying to insert one element using iterator at a specified position and then remove some elements again using iterator.But I get error when using the same iterator for both the operations.Here is my code -
#include <iostream>
#include <vector>
#include <cctype>
using namespace std;
int main()
{
vector <int> A(10);
for (int i=0;i<A.size();i++)
{
A[i]=i+1;
}
vector<int>::iterator p=A.begin();
p+=2;
A.insert(p,1,55);
A.erase(p,p+2);
for (int i=0;i<A.size();i++)
{
cout << A[i] <<"\n";
}
return 0;
}
This gives me the following output:
*** Error in `./temp': double free or corruption (out): 0x00000000017d4040 ***
55
3
4
5
6
7
8
9
10
Aborted (core dumped)
If I add following two lines before A.erase I get the correct answer.
p=A.begin();
p+=2;
A.erase(p,p+2);
So, if p still points to the same element as its value has not been changed, why do I need to again set the value of p.
after inserting/erasing from std::vector all existing iterators are invalidated and should not be used (using them will lead to undefined behavior)
remember, that changing items contained by vector may lead to memory allocation etc., so old iterators can point to deallocated memory (like pointers)
So when you add lines you mention and reinitialize iterator - everything is ok. But after insert existing p is no longer valid.
Check paragraphs about 'iterator invalidation' in: http://en.cppreference.com/w/cpp/container/vector/erase and http://en.cppreference.com/w/cpp/container/vector/insert.
You might consider adding call to reserve to ensure that no reallocation happens on insert but in my opinion such code would still be error prone and harder to maintain.
According to standard (n4296 C++14) [23.3.6.5/1] insert operation for vector invalidates iterators - but not always:
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens,
all the iterators and references before the insertion point remain valid.
and for erase [23.3.6.5/3]
Effects: Invalidates iterators and references at or after the point of the erase.
These are rules, behaviour you are seeing as correct is actually UB (undefined behaviour) - which means it might look like it works even if in 99% of times. Also it depends on implementation in your compiler.
Related
I'm trying to make a vector of pointers whose elements are pointing to vector of int elements. (I'm solving a competitive programming-like problem, that's why it sounds kinda nonsense).
but here's the code:
#include <bits/stdc++.h>
using namespace std;
int ct = 0;
vector<int> vec;
vector<int*> rf;
void addRef(int n){
vec.push_back(n);
rf.push_back(&vec[ct]);
ct++;
}
int main(){
addRef(1);
addRef(2);
addRef(5);
for(int i = 0; i < ct; i++){
cout << *rf[i] << ' ';
}
cout << endl;
for(int i = 0; i < ct; i++){
cout << vec[i] << ' ';
}
}
When I execute the code, it's showing weird behaviour that I don't understand. The first element of rf (vector<int*>) seems not pointing to the vec's (vector<int>) element, where the rest of the elements are pointing to it.
here's the output when I run it on Dev-C++:
1579600 2 5
1 2 5
When I tried to run the code here, the output is even weirder:
1197743856 0 5
1 2 5
The code is intended to have same output between the first line and the second.
Can you guys explain why it happens? Is there any mistake in my implementation?
thanks
Adding elements to a std::vector with push_back or similar may invalidate all iterators and references to its elements. See https://en.cppreference.com/w/cpp/container/vector/push_back.
The idea is that in order to grow the vector, it may not have enough free memory to expand into, and thus may have to move the whole array to some other location in memory, freeing the old block. That means in particular that your pointers now point to memory that has been freed, or reused for something else.
If you want to keep this approach, you will need to resize() or reserve() a sufficient number of elements in vec before starting. Which of course defeats the whole purpose of a std::vector, and you might as well use an array instead.
The vector is changing sizes and the addresses you are saving might not be those you want. You can preallocate memory using reserve() and the vector will not resize.
vec.reserve(3);
addRef(1);
addRef(2);
addRef(5);
The problem occurs when you call vec.push_back(n) and vec’s internal array is already full. When that happens, the std::vector::push_back() method allocates a larger array, copies the contents of the full array over to the new array, then frees the old/full array and keeps the new one.
Usually that’s all you need, but your program is keeping pointers to elements of the old array inside (rf), and these pointers all become dangling/invalid when the reallocation occurs, hence the funny (undefined) behavior.
An easy fix would be to call vec.reserve(100) (or similar) at the top of your program (so that no further reallocations are necessary). Or alternatively you could postpone the adding of pointers to (rf) until after you’ve finished adding all the values to (vec).
Just do not take pointer from a vector that may change soon. vector will copy the elements to a new space when it enlarges its capacity.
Use an array to store the ints instead.
I came across this error when I was using iterators with vector STL.
Code:-
#include <iostream>
#include <vector>
void print_vec(std::vector<int> vec)
{
auto itr = vec.begin();
while (itr != vec.end())
std::cout << *itr++ << " ";
std::cout << std::endl;
}
int main(int argc, char* argv[])
{
std::vector<int> vals = { 1, 2, 3, 4, 5, 6 };
print_vec(vals);
auto it = vals.begin();
vals.insert(it, 100);
print_vec(vals);
it += 4;
std::cout << *it << "\n";
vals.insert(it, 200);
print_vec(vals);
return 0;
}
Output:-
1 2 3 4 5 6
100 1 2 3 4 5 6
5
0 100 1 2 3 4 5 6
double free or corruption (out)
Aborted (core dumped)
I do not understand why this is happening: is it an issue with the iterator after the vector is modified? I think that my intuition regarding iterators is flawed.
When a std::vector is resized, the iterator becomes invalid because it refers to the vector before resizing which may include moving the vector to a different memory location in order to maintain a contiguity. In that case it will not be automatically updated to refer to the new location.
The result in this case (because it is undefined behaviour) is that the value 200 was not inserted in the vector, but it was written to some other heap memory that the vector previously occupied that has since been deallocated - i.e. you corrupted the heap. The vector is destroyed on exit from main, and at that time the system heap management fails and the error is reported (after the fact).
Reassigning the iterator following the insertion resolves the issue:
it = vals.insert(it,100);
...
The output of the single item reports 5 rather then 4 as you might expect because it is simply reading the old vector memory not yet reused and overwritten with something else.
You can observe the error by comparing:
vals.insert(it,100);
std::cout<<std::distance(it,vals.begin())<<'\n' ;
with
it = vals.insert(it,100);
std::cout<<std::distance(it,vals.begin())<<'\n' ;
Your results are likely to differ but at https://www.onlinegdb.com/ when I performed the test, the distance between the iterator position and the vector start in the first case was 16 - demonstrating that the iterator no longer refers to an element in the current vector state. In the second case the distance is zero - the iterator refers to the start of the new vector state.
If the capacity of vals were extended before taking the iterator by for example:
vals.reserve(10);
Then the insertion of a single element would not cause a mem-move, and the failure would not have occurred - that does not make it a good idea not to update the iterator though - it is merely to indicate what is happening under the hood.
...is it an issue with the iterator after the vector is modified?
Yes!
From the cppreference page on std::vector.insert() (bolding mine):
Causes reallocation if the new size() is greater than the old
capacity(). If the new size() is greater than capacity(), all
iterators and references are invalidated. Otherwise, only the
iterators and references before the insertion point remain valid. The
past-the-end iterator is also invalidated.
Consider this program:
#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
vector<double> a;
while (a.size() <= 10)
a.push_back(2);
cout << a[15] << endl;
}
I would expect it to crash when asked to access 15-th element of a, but it does not (outputs 0 on my system). What gives? Is this some new feature in gcc or something?
You are accessing an invalid memory location which leads to undefined behavior. So anything might happen.
This references says that it
Returns a reference to the element at position n in the vector
container.
A similar member function, vector::at, has the same behavior as this
operator function, except that vector::at signals if the requested
position is out of range by throwing an exception.
So, this might or might not crash.
the operator[] does no bounds checking, and in this case you were *un*lucky and the address at that location could be read without causing a run-time error.
Because the growth of the vector could be larger than 15 when you access it?
Check the capacity().
it is not strange..a vector grows it's size when it is inserted a.size()-1 element..so now you have kind of 20 elements.. (maybe not 20 but more than 11 (= )
I the memory you are reading is inside your program it won't crash, c++ does not bounds checking
Dream output:
/* DREAM OUTPUT:
INT: 1
TESTINT: 1
TESTINT: 2
TESTINT: 23
TESTINT: 24
TESTINT: 25
TESTINT: 3
TESTINT: 4
TESTINT: 5
TESTINT: 6
INT: 23
INT: 24
INT: 25
INT: 3
INT: 4
INT: 5
INT: 6
Problem
ERROR 1: Not erasing the '2' causes a bizzare effect.
ERROR 2: Erasing the '2' causes memory corruption.
Code
#include <cstdlib>
#include <iostream>
#include <vector>
int main(int argc, char* argv[])
{
std::vector<int> someInts;
someInts.push_back(1);
someInts.push_back(2);
someInts.push_back(3);
someInts.push_back(4);
someInts.push_back(5);
someInts.push_back(6);
for(std::vector<int>::iterator currentInt = someInts.begin();
currentInt != someInts.end(); ++currentInt)
if(*currentInt == 2)
{
std::vector<int> someNewInts;
someNewInts.push_back(23);
someNewInts.push_back(24);
someNewInts.push_back(25);
someInts.insert(currentInt + 1, someNewInts.begin(), someNewInts.end());
//someInts.erase(currentInt);
for(std::vector<int>::iterator testInt = someInts.begin();
testInt != someInts.end(); ++testInt)
std::cout << "TESTINT: " << *testInt << '\n';
}
else
std::cout << "INT: " << *currentInt << '\n';
return 0;
}
The code is pretty self-explanatory, but I'd like to know what's going on here. This is a replica using ints of what's happening in a much larger project. It baffles me.
Inserting elements into a vector causes the iterators asociated with it to be invalid, since the vector can grow and thus it reallocates its internal storage space.
As someInts.erase(currentInt); invalidates currentInt you can't use it until you set it right.
It so happens that erase() returns a valid iterator in the list to continue with.
An iterator that designates the first element remaining beyond any elements removed, or a pointer to the end of the vector if no such element exists.
Try
currentInt = someInts.erase(currentInt);
which would put the outer loop at '23' the start of your test data and step to '24' for the next loop.
You need to understand the differences between the stl collections.
A Vector is a continuous (usually) block of memory. Whem you insert into the middle, it tries to be helpful by re-allocating enough memory for the existing data plus the new, then copying it all to the right places and letting you continue as if nothing had happened. However, as you're finding - something has happened. Your iterators that used to refer to the old memory block, are still pointing there - but the data has been moved. You get memory errors if you try to use them.
One answer is to determine where the iterator used to point, and update it to point to the new location. Typically, people use the [] operator for this, but you can use begin() + x (where x is the index into the vector).
Alternatively, use a collection whose iterators are not invalidated by inserting. The best one for this is the list. Lists are constructed from little blocks of memory (1 per item) with a pointer to the next block along. This makes insertion very quick and easy as no memory needs to be modified, just the pointers to the blocks either side of the new item. Your iterator will still be valid too!
Erasing is just the same, except once you delete the item your iterator refers to, its invalid (obviously) so you cannot make any operation on it. Even ++ operator will not work as the memory might have changed in a vector, or the list pointers be different. So, you can first get an iterator to the next element, store it and then use that once you've deleted an item, or use the return value from the erase() method.
If you were to use list as the collection instead of vector, you would not get random-access and it might use more memory but you would have constant-time insertion in the middle of the collection and doing so would not invalidate your iterators.
The exception would be the one you were erasing, so you would not be able to ++ it at the end of the loop. You would have to handle this situation by storing a copy of its next element.
I understand why this causes a segfault:
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
int iArr[5] = {1, 2, 3, 4, 5};
int *p = iArr;
copy(p, p+5, v.begin());
return 0;
}
But why does this not cause a segfault?
#include <algorithm>
#include <vector>
using namespace std;
int main()
{
vector<int> v;
int iArr[5] = {1, 2, 3, 4, 5};
int *p = iArr;
v.reserve(1);
copy(p, p+5, v.begin());
return 0;
}
Both are wrong as you are copying to empty vector and copy requires that you have space for insertion. It does not resize container by itself. What you probably need here is back_insert_iterator and back_inserter:
copy(p, p+5, back_inserter(v));
This is undefined behavior - reserve() allocates a buffer for at least one element and the element is left uninitialized.
So either the buffer is big enough and so you technically can access elements beyond the first one or it is not big enough and you just happen to not observe any problems.
The bottom line is - don't do it. Only access elements that are legally stored in the vector instance.
But why does this not cause a
segfault?
Because the stars aligned. Or you were running in debug and the compiler did something to "help" you. Bottom line is you're doing the wrong thing, and crossed over in to the dark and nondeterministic world of Undefined Behavior. You reserve one spot in the vector and then try to cram 5 elements in to the reserve-ed space. Bad.
You have 3 options. In my personal order of preference:
1) Use a back_insert_iterator which is designed for just this purpose. It is provided by #include <iterator>. The syntax is a bit funky, but fortunately a nice sugar-coated shortcut, back_inserter is also provided:
#include <iterator>
// ...
copy( p, p+5, back_inserter(v) );
2) assign the elements to the vector. I prefer this method slightly less simply because assign is a member of vector, and that strikes me as slightly less generic than using somethign from algorithm.
v.assign(p, p+5);
3) reserve the right number of elements, then copy them. I consider this to be a last ditch effort in case everything else fails for whatever reason. It relies on the fact that a vector's storage is contiguous so it's not generic, and it just feels like a back-door method of getting the data in to the vector.
This is wrong! it is undefined behavior to access memory you don't own, even if it works in an example. The reason, I think, is that std::vector would reserve more than one element.
Because you were unlucky. Accessing memory not allocated is UB.
Most likely because an empty vector doesn't have any memory allocated at all, so you are trying to write to a NULL pointer which normally leads to an instant crash. In the second case it has at least some memory allocated, and you are most likely overwriting the end of an array which may or may not lead to a crash in C++.
Both are wrong.
It would be wrong, by the way, even to copy 1 element to the vector that way (or to reserve 5 then copy that way).
The reason it most likely does not segfault is that the implementor felt it would be inefficient to allocate the memory for just 1 element just in case you wanted to grow it later, so maybe they allocated enough for 16 or 32 elements.
Doing reserve(5) first then writing into 5 elements directly would probably not be Undefined Behaviour but would be incorrect because vector will not have a logical size of 5 yet and the copy would almost be "wasted" as vector will claim to still have the size of 0.
What would be valid behaviour is reserve(5), insert an element, store its iterator somewhere, insert 4 more elements and look at the contents of the first iterator. reserve() guarantees that the iterators do not become invalidated until the vector exceeds that size or a call such as erase(), clear(), resize() or another reserve() is made.