erase in std::vector, debug, release - c++

std::vector<int> va; // and push_back 1~100
std::vector<int>::iterator i = va.begin();
for(i; i != va.end(); )
{
if((*i) == 5) va.erase(i);
else i++
}
This code is 100% crashed when debug runtime.
But don`t crash this code when release runtime.
Why this happen?
What is different debug and release mode in this code?

You have undefined behavior because you're using an invalid iterator (i is invalidated by the erase()).
Avoid the whole problem by using the Erase-remove Idiom:
va.erase(std::remove(va.begin(), va.end(), 5), va.end());

As others have pointed out, the crash is due to the invalidated iterator that you are continuing to use after calling va.erase().
Now, as to why it works in Release mode, is that in some cases the iterator for a std::vector<> in Release mode is a simple pointer into a dynamically allocated array. When you call erase, the iterator continues to point at the same element of the array while the contents of the array have been moved by the erase function. This is undefined behavior and Standard Library implementation specific, but very common. Under no circumstance should you rely on the behavior in portable code.
However, on some Standard Library implementations, Debug mode iterators perform checking and are more complicated than simple pointers. As such, they can detect that you are doing something that isn't legal and intentionally cause a crash, so that you can recognize your error.

vector::erase returns a new iterator, as it makes the current one invalid.
if((*i) == 5) va.erase(i);
should be
if((*i) == 5) i = va.erase(i);

Related

Vector gets iterated more times than size()

I've got this piece of code:
for (std::vector<Marker>::iterator it = markers.begin(); it != markers.end(); ++it) {
if (it->getDots().size() < 3) {
markers.erase(it);
}
}
In one of test inputs (the app does image analysis) I get a segfault. I tried to debug the code (to no avail) and noticed one thing. When asking gdb to p markers.size() i receive $9 = 3. So I would expect the loop to iterate three times, but surprisingly it does it (at least) 5 times. In fifth iteration there's a segfault. What I also noticed is that it's not the dereference of *it (here it->) that causes the error. It's specifically it->getDots(), which is a simple getter.
I write in C++ very rarely, so it might be some simple mistake, but neither my debugging, nor googling brought any solution. Could you help?
I'd like to emphasize, that on various different inputs (slightly different images) this function works properly, so it's even harder for me to trace the bug down.
vector::erase invalidates all iterators pointing to the element being erased, and all elements that follow. So it becomes invalid, and ++it expression on the next loop iteration exhibits undefined behavior.
The best way to code this logic is with erase-remove idiom.
The problem is this line:
markers.erase(it);
The iterator is invalidated. But that's okay, erase returns a valid iterator:
auto it = markers.begin();
while(it != markers.end())
{
if(it->getDots().size() < 3) {
it = markers.erase(it);
}
else ++it;
}
You need to update it when you erase:
it = markers.erase(it);
since erase will "change" the vector, and your current it is no longer valid (and only do it++ if when you didn't erase it).
However, the more common way to do this is to use this type of construction:
markers.erase(std::remove(markers.begin(), markers.end(), number_in), markers.end());

c++ stl vector iterator insert segmentation fault

std::vector<struct::event>::iterator it;
std::vector<struct::event>::iterator last=myvector.end();
for (it=myvector.begin(); it<=last; it++){
if(mysignal.declination<(*last).declination){
if (mysignal.declination>=(*it).declination && mysignal.declination<(*(it+1)).declination){
myvector.insert(it+1, mysignal);
break;
}
}
if (mysignal.declination>=(*last).declination){
myvector.push_back(mysignal);
break;
}
}
I have a vector called myvector with events that are sorted with the declination. now I want to add mysignal to this vector on the right place. but i always get a seg fault after a few events which refers to: if(mysignal.declination<(*last).declination). I just can't see what is wrong.
Your loop is wrong, read the docs:
Returns an iterator to the element following the last element of the container.
This element acts as a placeholder; attempting to access it results in undefined behavior.
You can't dereference end(), it provides a way of knowing that you have overrun the container, so your loop condition should be it != myvector.end(), and last is wrong as well.
As others have said, C++ iterators define a half-open interval
('[begin()...end())'), which is what you should probably be
using in most other cases as well. And although it works with
iterators from a vector, in general, iterators do not support
<= (nor <); the standard idiom for a loop is:
for ( auto current = container.begin();
current != container.end();
++ current ) ...
(In the most likely case that you cannot count on C++11, you'll
have to write out the full iterator type, rather than use
auto. Although auto is one of the few things from C++11
that seems to work with VC++11 and with recent versions of
g++, so if those are the only targets you're concerned with, and
you can be sure of always having very recent versions, you can
use it.)
Also, if you want to access the last element of the vector in
the loop, myvector.back() will return a reference to it.
(myvector.back() is undefined behavior if the vector is empty,
but if the vector is empty, you won't enter the loop.)
end() does not refer to the last element in the container, you need to change your condition as follows.
for (it=myvector.begin(); it != last; it++){
You have other broken logic as well that is dereferencing last that you need to fix.

C++ Errors with referenced object - how to debug?

Following the help in this question, I am using a reference to my Class 'Mover' to manipulate the object (as part of a set) in a vector. I am having issues however, and I cannot seem to identify what's causing it for sure. It appears that once I've reached 30-35 objects in my vector (added at pseudo-random intervals) the program halts. No crash, just halt, and I have to manually end the task (CTRL-C doesn't work).
My problem appears to lie in these bits of code. My original:
int main() {
std::vector< Mover > allMovers;
std::vector< Mover >::iterator iter = allMovers.begin();
//This code runs to the end, but the 'do stuff' lines don't actually do anything.
Mover tempMover;
//Other code
while(iter < allMovers.end()) {
tempMover = *iter;
//Do stuff with tempMover
//Add another tempMover at a random interval
allMovers.push_back(CreateNewMover());
iter++;
}
//Other code
}
My update after the previous question linked to above:
int main() {
std::vector< Mover > allMovers;
std::vector< Mover >::iterator iter = allMovers.begin();
//This code crashes once about 30 or so items exist in the vector, but the 'do stuff' lines do work.
//Other code
while(iter < allMovers.end()) {
Mover& tempMover = *iter;
//Do stuff with tempMover
//Add another tempMover at a random interval
allMovers.push_back(CreateNewMover()); //Crashes here.
iter++;
}
//Other code
}
Any ideas of how to track this down? I have std::couts all over the place to flag where the code is for me. The crash (while happens at a varied number of objects) always crashes on the push_back(), despite having worked successfully multiple times in the same run before the crash.
EDIT
While I accept and (think) I understand the answer re: iterators, what I don't understand is why the code DOES work completely when I am not using a reference to the object? (First code block).
Another EDIT
In case anyone was looking for this specifically, part of my question was not addressed: "How to debug?" As a C++ newbie, I was unaware of the gdb debugger (using MinGW). Now that I've learned about it, it has been very helpful in finding the source of these issues.
When a vector reallocates its memory, all iterators are invalidated (along with any reference or pointer to any element). So sometimes your push_back will invalidate iter, and trying to use it afterwards gives undefined behaviour.
The simplest fix is to use an index rather than an iterator. Alternatively, if you can calculate an upper bound for the maximum size of the vector, you could call reserve before the loop to ensure it never reallocates. Or you could use std::list, whose iterators are preserved when new elements are inserted.
UPDATE: Regarding your edit, both give undefined behaviour. It might be that, in the first case, you don't crash because you don't access a dangling reference (while accessing tempMover in the second might very well crash), and then the memory happens to be reallocated at a lower address than before, so the while condition (which uses < rather than the more conventional !=) exits the loop immediately. Or something completely different could be happening - that's the nature of undefined behaviour.
You are (probably) doing it wrong.
The thing is, mixing iteration over a container and manipulation of the container structure (here adding objects) is extremely error-prone.
Whenever you add an element in allMovers, there is a risk that iter is invalidated. Any usage of iter after it has been invalidated is Undefined Behavior.
It is possible to do it correctly:
iter = allMovers.insert(allMovers.end(), CreateNewMover());
however it's just a bad idea in general.
My advice would be to ban this kind of code from your code base altogether. Every single occurrence is a bug in the making. Find another algorithm.
From documentation for push_back():
If new size() is not larger than capacity(), no iterators or references are invalidated. Otherwise all iterators and references are invalidated.
When you reach 30 or some objects new size() > capacity(), resulting in invalidation of the iterator iter, which is derefenced causing undefined behaviour.
You might probably need to change the line containing the while statement:
while(iter != allMovers.end()) {
the < operator seems to work fine with a vector usually, but I had better results using != which works with other containers and also seems to be used in more example code out there.
Update
You may replace the while loop with an equivalent for loop like this:
for(std::vector<Mover>::iterator iter = allMovers.begin(); iter != allMovers.end(); ++iter)
{
This has the advantage that the increment of the iterator iter "has its place" and is less likely to be forgotten.
Update 2
If I understand your example above, you'd like to fill the container with some content. I suggest (as others did) to get rid of the iterator altogether.
int main()
{
std::vector< Mover > allMovers;
//Other code
while(1) // this loop will add new movers as long as it succeeds to create one
{
Mover new_mover = CreateNewMover();
if ( IS EMPTY (new_mover) ) // pseudocode. Check if the previous
break; // CreateNewMover() succeeded.
allMovers.push_back(new_mover);
}
//Other code
}

std::list<>: Element before l.begin()

Short question: Is the following code unsafe using other compilers than I do (mingw32), or is it valid to use?
list<int> l;
/* add elements */
list<int>::iterator i = l.begin();
i--;
i++;
cout << *i << endl;
...or in other words: is i defined to point to l.begin() after this?
Yes, the code is unsafe. Once you attempt to move before begin() you have caused undefined behavior. Attempting to move "back again" may not work.
A std::list traverses its contents via linked list pointers, so pointer arithmetic is not used to calculate a correct position. The previous position from .begin() will have no data and shouldn't provide any valid traversal mechanisms.
Containers like std::vector have random access iterators and would use pointer arithmetic under the covers, so they would probably give the right result (no problem), but its still a bad idea.
So, it shouldn't work, its undefined, and don't do it even if it does work somehow :)

Referencing invalid memory locations with C++ Iterators

I am a big fan of GCC, but recently I noticed a vague anomaly. Using __gnu_cxx::__normal_iterator (ie, the most common iterator type used in libstdc++, the C++ STL) it is possible to refer to an arbitrary memory location and even change its value without causing an exception! Is this expected behavior? If so, isn't a security loophole?
Here's an example:
#include <iostream>
using namespace std;
int main() {
basic_string<char> str("Hello world!");
basic_string<char>::iterator iter = str.end();
iter += str.capacity() + 99999;
*iter = 'x';
cout << "Value: " << *iter << endl;
}
Dereferencing an iterator beyond the end of the container from which it was obtained is undefined behavior, and doing nothing is just a possibility there.
Note that this is a question of compromise, it is nice having iterators check for validity for development, but that adds extra operations to the code. In MSVS iterators are by default checked (they will verify that they are valid and fail hard when they are used in a wrong way=. But that also has an impact in runtime performance.
The solution that Dinkumware (STL inside VS) provides (checked by default, can be unchecked through compiler options) is in fact a good choice, the user selects whether he wants slow safe iterators or fast unsafe versions of it. But from the point of view of the language, both are valid.
No, this is not a problem. Keep in mind that typical iterator usage is:
for ( type::const_iterator it = obj.begin(); it != obj.end(); ++it ){
// Refer to element using (*it)
}
Proper iterator usage requires one to check against the end() iterator. With random access iterators such as the one you are using, you can also use < and > with the iterators against end(). C and C++ don't typically do bounds checking as in Java, and it is your place to ensure that you do so.
C++ generally has a philosophy of not making you pay for what you don't use. It is up to you to validate that you're using iterators properly. For a random-access iterator, you can always test it:
if (iter < str.begin() || iter >= str.end())
throw something;
You got lucky. Or unlucky. Using your exact example, I segfaulted.
$ ./a.exe
11754 [main] a 4992 _cygtls::handle_exceptions: Error while dumping state (probably corrupted stack)
Segmentation fault (core dumped)
Undefined behavior can mean different things on different compiles, platforms, days. Perhaps when you ran it, the address created by all that adding ended up in some other valid memory space, just by chance. Maybe you incremented from the stack to the heap for example.