Decrementing iterator after each loop iteration shows weird behavior - c++

I created a program to try to practice on the semantics of the list data structure. I noticed a weird difference in the following pieces of code:
First code:
#include<iostream>
#include<list>
using namespace std;
int main() {
list<int> l;
int n = 100;
for(int i = 0; i < n; i++) {
l.push_back(i);
}
list<int>::iterator it = l.end();
it--;
for(; !l.empty(); it--) {
cout << "the size of l is " << (int) l.size() << endl;
l.erase(it);
}
}
Second code:
#include<iostream>
#include<list>
using namespace std;
int main() {
list<int> l;
int n = 100;
for(int i = 0; i < n; i++) {
l.push_back(i);
}
list<int>::iterator it = l.end();
it--;
for(; !l.empty();) {
cout << "the size of l is " << (int) l.size() << endl;
l.erase(it--);
}
}
The objective of both pieces of code is simple - to simply erase all the elements in a list.
The only difference between them is the place where the list iterator is decremented.
In the first code sample, I used the for-loop control flow to decrement the iterator. In the second, I used the post-decrement operator to decrement the iterator.
Based on my understanding, the above code samples should be equivalent because I decrement the iterator immediately after I erase an element from the list. Furthermore, according to the STL docs, only the iterator to the erased element in the list is invalidated. So there should not be any undefined behavior.
The problem is, the second code sample works as expected - it stops after erasing all elements in the list. However, for the first sample, the list size could even become negative?! When I tried increasing the initial number of elements in the list, the first program crashes halfway.
Could someone kindly advise me on why these code samples behave differently?

The 1st code has undefined behavior. As you said, erase makes the iterator invalid, the it-- evaluated after that leads to UB.
The 2nd code is fine; note the evaluation order is different. it-- will decrement the iterator, then return the original value (that's the point of post-decrement operator). The original value is passed to erase later. Decrement happens before erase so it's fine.

Related

How come my vector array won't output anything after I erase an element?

Recently I've started learning C++, and everyday I do a C++ practice exercise to understand the language more. Today I was learning Vector Arrays and I hit a roadblock.
I'm trying to make a simple program that takes an array, puts it into a vector, then removes all the odd numbers. But for some reason when I erase an element from the vector, and output the modified vector, it doesn't output anything.
If somebody could guide me to the right direction on what I'm doing wrong, that would be great!
remove.cpp
#include <iostream>
#include <vector>
using namespace std;
class removeOddIntegers {
public:
void removeOdd(int numbs[]) {
vector<int> removedOdds;
for(int i = 0; i < 10; ++i) {
removedOdds.push_back(numbs[i]);
}
for(auto i = removedOdds.begin(); i != removedOdds.end(); ++i) {
if(*i % 2 == 1) {
removedOdds.erase(removedOdds.begin() + *i);
std::cout << "Removed: " << *i << endl;
}
}
for(auto i = removedOdds.begin(); i != removedOdds.end(); ++i) {
std::cout << *i << endl; //doesn't output anything.
}
}
};
main.cpp
#include <iostream>
#include "remove.cpp"
using namespace std;
int main() {
removeOddIntegers r;
int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
r.removeOdd(numbers);
return 0;
}
Now, I understand that I could just filter through the array, and only push_back the even numbers to the vector, and quite frankly, that works like a charm. But I want to understand why my method doesn't work. How come when I remove an element from the vector, it just fails to output anything?
Thanks in advance!
There's a few things wrong, but they mostly boil down to the same fundamental issue. You are violating iterator guarantees of std::vector::erase:
Invalidates iterators and references at or after the point of the erase, including the end() iterator.
You do this both when dereferencing the deleted iterator to display your "removed" message, and also when calling ++i for the loop.
In addition, your call removedOdds.erase(removedOdds.begin() + *i); is wrong, because it's using the actual value in the vector as an offset from the beginning. That assumption is completely wrong.
The proper way to erase an element at an iterator and retain a valid iterator is:
i = removedOdds.erase(i);
Here is your loop with minimum changes required to fix it:
for (auto i = removedOdds.begin(); i != removedOdds.end(); ) {
if (*i % 2 == 1) {
std::cout << "Removed: " << *i << endl;
i = removedOdds.erase(i);
} else {
++i;
}
}
Notice how the iterator is advanced in the body of the loop now. You can do a thought experiment to think about why. Or you can try doing it the wrong way and use an input like { 1, 3, 5, 7, 9 } to demonstrate the problem.
This is still not the idiomatic way to remove elements from a vector. As you alluded to, elements should be swapped to the end of the vector. The reason for this is that std::vector::erase is a linear operation that must shuffle the entire remainder of the vector. If you do this multiple times, you essentially have O(N^2) time complexity.
The recommended approach is to use std::remove_if:
removedOdds.erase(removedOdds.begin(),
std::remove_if(removeOdds.begin(), removeOdds.end(),
[](int n) { return n % 2 == 1; }));
The flaw in the shown algorithm is more easily observed with a much simpler example:
for(int i = 0; i < 2; ++i) {
removedOdds.push_back(numbs[i]);
}
This initializes the vector with just two values: 0 and 1. This is small enough to be able to follow along in your head, as you mentally execute the shown code:
for(auto i = removedOdds.begin(); i != removedOdds.end(); ++i) {
Nothing interesting will happen with the first value, 0, that gets iterated here. ++i increments the iterator to point to the value 1, then:
if(*i % 2 == 1) {
removedOdds.erase(removedOdds.begin() + *i);
std::cout << "Removed: " << *i << endl;
}
This time erase() removes 1 from the vector. But that's what i is also pointing to, of course. Then, if you look in your C++ reference, you will discover that std::vector::erase:
invalidates iterators and references at or after the point of the erase,
including the end() iterator.
i is now "at the point of the erase", therefore, i is no longer a valid iterator, any more. Any subsequent use of i becomes undefined behavior.
And, i immediately gets used, namely incremented in the for loop iteration expression. That's your undefined behavior.
With the original vector containing values 0 through 9: if you use your debugger it will show all sorts of interesting kinds of undefined behavior. You can use your debugger to see if the shown code ever manages to survive when it encounters a higher odd value, like 7 or 9. If it does, at that point this vector will obviously be much, much smaller, but removedOdds.erase(removedOdds.begin() + *i); will now attempt to remove the 7th or the 9th value in a vector that's about half its size now, a completely non-existent value in the vector, with much hilarity ensuing.
To summarize: your "method doesn't work" because the algorithm is fundamentally flawed in multiple ways, and the reason you get "no output" is because the program crashes.

any clear explanation of this?

I'm trying to learn C++ vectors.. Here is the code:
#include <iostream>
#include <vector>
using namespace std;
int main(){
vector <int> vec;
for(int i=0; i<=10; i++){
vec.push_back(i);
}
for(auto i=vec.begin(); i!=vec.end();i++){
cout<<*i<<" ";
}
}
Can anybody tell me what this part is?
for(auto i=vec.begin(); i!=vec.end();i++){
cout<<*i<<" ";
}
I've searched the Internet but couldn't find a clear explanation.
Ok, it prints the numbers that we put it in the vector, but can I get a more technical explanation?
for(auto i=vec.begin(); i!=vec.end();i++){
cout<<*i<<" ";
}
This is just the iterators in C++.
begin() function is used to return an iterator pointing to the first element of the vector.
Similarly, end() function is used to return an iterator pointing past the last element of the vector.
auto just deduces the type of the variable i. You could have also specified it as std::vector<int>::iterator i = vec.begin() . That's the type of the iterator you are using to loop over the vector.
In the above piece of code, you are basically iterating from the beginning of the vector until the end of the vector.
Inside the loop, you are just dereferencing the iterator and printing the value at the current position where the iterator is.
What the above piece of code is doing is basically the same as the following type of loop, which uses indexing to loop over the array:
for(size_t i = 0; i != vec.size() ; i++){
cout << vec[i] << " ";
}
You should read more about iterators, as they are a core concept in C++. You can read more about them here:
iterators
std::vector.begin()
std::vector.end()
This is an iterator to the first element in the vector: vec.begin()
An iterator to one-past the last element of the vector: vec.end()
auto deduces the type of i from vec.begin(), its an iterator. We really do not need to care about the exact type.
We only need to know that we can increment it: i++.
And compare two iterators with each other to check if we are at the end: i != vec.end().
And we can derference iterators to get the element the "point to": *i.
Without iterators the loop could be written as:
for (size_t i=0; i<vec.size(); ++i) {
std::cout << vec[i];
}
That part simply prints all the elements of the vector. auto automatically determines what data structure it is given the parameters that define it. In this case, it is being used as a vector<int>::iterator. Mostly, this is used in other data structures, such as a map or a set, since those don't support random access. In a vector, you can simply do
for(int i = 0; i < vec.size(); i++)
{
cout << vec[i] << " ";
}

C++ code works fine on desktop but not on the laptop

This simple code for some reason works perfectly fine on my desktop but when I try it on my laptop only the first part (printing the elements of vector) works then the program ends and instead of saying "Process finished with exit code 0" it says
"Process finished with exit code -1073741819 (0xC0000005)". I don't know what's wrong with my laptop. Can anyone help me?
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> array{1, 2, 3, 4, 5};
vector<int>::iterator it;
int arraysize;
for (int i = 0; i < array.size(); i++) {
cout << array[i] << endl;
}
cout << " " << endl;
for (it = array.begin(); it < array.end(); it++) {
if(*it%2==0){
array.erase(it);
it--;
}
}
arraysize=array.size();
cout<<"size:"<<arraysize<<endl;
for (int i = 0; i < array.size(); i++) {
cout << array[i] << endl;
}
return 0;
}
The problem is not with the computer but with the code.
array.erase(it); invalidates the iterator it, and any subsequent use of it has undefined behaviour.
The worst kind of undefined behaviour is the one that appears to work.
erase returns an iterator to the element after the erased one, and you should use that.
for (it = array.begin(); it < array.end(); it++) {
if(*it%2==0){
it = array.erase(it);
it--;
}
}
or
it = array.begin();
while (it < array.end()) {
if(*it%2==0){
it = array.erase(it);
}
else {
it++;
}
}
This happens because of iterator invalidation, when you erase an element of the vector the iterator it becomes invalidated leading to undefined behaviour, you can read more about this here Iterator invalidation rules
Your program has undefined behaviour, you decrement an invalid iterator
array.erase(it); // it becomes invalid
it--; // Undefined
You can avoid this by removing elements with the "erase-remove" pattern
auto is_even = [](int i) { return i%2==0; };
array.erase(std::remove_if(array.begin(), array.end(), is_even), array.end());
Or in C++20
std::erase_if(array, is_even);
There is nothing wrong with your laptop. The problem is with the code. When you erase something from the vector, it invalidates the preexisting iterators following the erased elements. You may want to use the return value of erase, which references the new reallocated location of the erased element's successor.

std::bad_alloc error in C++ vector in merge sort

I am trying to implement merge sort using vector in c++, i am getting following error -
terminate called after throwing an instance of 'std::bad_alloc'.
There were other solutions for bad_alloc but it didn't help.
problem is merging function-
#include<iostream>
#include<vector>
using namespace std;
void print(vector<int> v)
{
for(int i=0; i<v.size(); i++)
cout << v[i] << " ";
cout << endl;
}
vector<int> merging(vector<int> left, vector<int> right)
{
vector<int> result;
while((int)left.size()>0 && (int)right.size()>0)
{
if((int)left.front()<=(int)right.front()){
result.push_back(left.front());
//left.erase(left.begin());
}
else{
result.push_back(right.front());
//left.erase(right.begin());
}
}
while((int)left.size()>0){
for(int j=0; j<(int)left.size(); j++)
result.push_back(left[j]);
}
while((int)right.size()>0){
for(int k=0; k<(int)right.size(); k++)
result.push_back(right[k]);
}
cout << "check merging";
return result;
}
int main()
{
vector<int> a, b, result;
a.push_back(38);
a.push_back(27);
b.push_back(43);
b.push_back(3);
cout << a.front() << endl;
print(a);
cout << endl;
print(b);
cout << endl;
result = merging(a, b);
print(result);
}
thank you
From the page on std::bad_alloc
Type of the exceptions thrown by the standard definitions of operator new and operator new[] when they fail to allocate the requested storage space.
You're running out of memory and an exception is being thrown when you can't allocate anymore.
Try taking a look at the exit conditions for your loops.
while((int)left.size()>0 && (int)right.size()>0)
This loop won't exit until both left and right are empty, however you're never changing their size (the erase calls are commented out).
The other two while loops have similar issues.
The first while loop never terminates. It keeps adding elements to result until the memory is exhausted.
There are other issues with your code:
In the case of left.front() <= right.front you never remove the element you put in result from left, it is commented out.
In that else, you do not remove from right either. The commented out code would remove from left, mixing an iterator from right into an erase from left, what is technically called "undefined behavior" meaning that your program is toast.
Also, why do you cast to int everywhere? that's so unnecessary
Even if you would erase from the front of left and right, you would use vectors for removing from the front, operations that are not supported efficiently by a vector. There is no need to do that, you can traverse left and right using iterators, checking instead of whether they are empty for whether the left iterator reached the end of left and whether the right iterator reached the end of right
You have an infinite loop.
while((int)left.size()>0 && (int)right.size()>0)
{
if((int)left.front()<=(int)right.front()){
result.push_back(left.front());
//left.erase(left.begin());
}
else{
result.push_back(right.front());
//left.erase(right.begin());
}
}
Is never going to end as you never remove any elements from left

Deleting elements from a vector that meet a condition

I am trying to program the Sieve of Eratosthenes, but I am not sure how to delete elements from the vector I made given a specific condition. Does anyone know how to achieve this? Here is my code:
#include <iostream>
#include <vector>
using namespace std;
int prime(int n);
int prime(int n)
{
vector<int> primes;
for(int i = 2; i <= n; i++)
{
primes.push_back(i);
int t = i % (i + 1);
if(t == 0)
{
delete t; // is there a way of deleting the elements from
// the primes vector that follow this condition t?
}
cout << primes[i] << endl;
}
}
int main()
{
int n;
cout << "Enter a maximum numbers of primes you wish to find: " << endl;
cin >> n;
prime(n);
return 0;
}
Your algorithm is wrong:
t = i % (i + 1);
is
i
which is always != 0 because i is larger than 1.
By the way if you absolutely want to remove the t-th element you have to be sure that the vector is not empty and then you do:
primes.erase(primes.begin()+t);
Even if you fix the algorithm your approach is inefficient: erasing an element in the middle of a vector means copying back of one position all the ones following the erased element.
You don't usually want to delete elements in the middle of a Sieve of Eratosthenes, but when you do want to, you usually want to use the remove/erase idiom:
x.erase(std::remove_if(x.begin(), x.end(), condition), x.end());
std::remove basically just partitions the collection into those that don't meet the specified condition, followed by objects that may have been used as the source of either a copy or a move, so you can't count on their value, but they are in some stable state so erasing them will work fine.
The condition can be either a function or a functor. It receives (a reference to a const) object that it examines and determines whether it lives or dies (so to speak).
Find here a c++ pseudocode for the sieve algorithm. Once you've understood the algorithm you can start working on this.
primes(vector& primes, size_t max){
vector primesFlag(1,max);
i=1
while(i*i<max){
++i;
for(j=i*i; j < max; j+= i){
primesFlag[j] = 0;
}
}
primes.clear()
primes.reserve(...);
for(j >= 2;
if primesFlag[j] = 1
primes.push_back(j);
}