I want to insert a pair< string, vector<float> > into a map, first it works, but after several loops, it cannot insert any more and throw me a segmentation fault. Can anybody give a possible reason?
Btw: I first read a file and generate the map (about 200,000 elements) and I read another file and update the old map. the error occurs while the updating step.
Can anybody help me with the info I gave above? Thanks a lot
The code is pretty long.....I just erase the previous key and then insert a new one, it seems not complicated.....but drives me crazy....could you guess what happened here?
Thanks A lot for all your answers! And I found it is really a good place for solving problems. Thanks again, I'll try to simplify my codes and add it here today or tomorrow.
Update: I used the code from MSN and it works, thanks a lot that you solved my problem without seeing my code......also many thanks to other kind-hearted people here! However, i can only choose one as the answer.
Are you inserting using the iterator you called erase() on? Or using that iterator in any way? After erase(p) is called, p is invalidated.
Without more information, it's not easy to say, but what are you inserting? Could it simply be that you run out of memory? Although, I do think normal C++ would throw an exception in that case, are you using any custom allocators, malloc, or arrays on the stack which are overrun perhaps?
Perhaps a snippet of code describing what you do could be helpful in determining the cause of your problem.
could you guess what happened here?
You're abusing memory in some way.
There are a lot of ways to do that, in C++!
Rather than guess, and without reading your code, I suggest run the kind of platform-specific debugger which will detect this problem, for example valgrind.
Your alternative is to make the problem smaller: reproduce the problem in only a few lines of code, which you can then post for people to look at.
The type in question is pair<string, vector<float> >. You will be copying that pair on every insert. If either the string or the vector are big then you could be running out of memory.
Edit: to fix running out of memory, you can change how you insert key-value pairs to:
pair<map::iterator, bool> insert_result= map.insert(make_pair(name, vector<float>());
if (insert.second) { insert_result.first->second.swap(vector_read_in); }
That will ensure that you do not copy memory, only move it.
It easily happens if you either modify the keys of the elements already in the data structure or if you have a bad compare function, which misleads the search algorithm.
If you can detect which concrete insert operation causes the seg.fault, then try debugging/logging with what values the compare function is called.
Alternatively, you should print the contents of the map before the erroneous insert, the keys will probably not be in order.
please post some code, you cant expect us to debug your problem on guess work alone.
...but ill give it a stab anyway :) Also what compiler, and system are you doing this on?
If you are reading the data in a loop you may run out of stack space which would cause a seg fault.
Ill edit my answer if you post some code.
Remember that if you are looping through the map, finding stuff to delete, save off the key for later deletion. If you delete while iterating, you run the risk of invalidating the iteration loop.
std::map<string, vector<float> >::iterator iter = my_map.begin();
while (iter != my_map.end()) {
if (somethingBadAboutItem(iter)) {
my_map.erase(iter); // this can mess up my_map iteration above
// and cause bad data access later
}
++iter;
}
instead, try
std::map<string, vector<float> >::iterator iter = my_map.begin();
std::vector<string> keys_to_delete;
while (iter != my_map.end()) {
if (somethingBadAboutItem(iter)) {
keys_to_delete.push_back(iter->first);
}
++iter;
}
for (std::size_t i = 0; i < keys_to_delete.size(); ++i) {
iter = my_map.find(keys_to_delete[i]);
my_map.erase(iter);
}
I am interested if other people have found something more elegant than this, but this is my preferred technique.
Bill: Have you tried something like this:
for(std::map<string, vector<float> >::iterator iter = my_map.begin(); iter != my_map.end();) {
if(somethingBadAboutItem(iter)) {
my_map.erase(iter++);
} else {
++iter;
}
}
The key is to postincrement the iterator when deleting, so it's still valid when incremented but you erase (and hence invalidate) a copy pointing to the previous item.
I'm not sure this technique necessarily works for all STL containers; I can't remember all the invalidation rules offhand (seems unlikely to work for vector for example, but you're normally better off using remove_if for those for non-trivial sizes) but it should be fine for map.
Related
I need to be able to populate a list one time and then return the index of the item quickly. For instance if I had 10 people in a list i need to be able to look up the person and return their number(1-10) in the list. I thought about several different ways but so far none have worked. I have a vector populated with my data now I just need to be able to get to the index. I am not set on a vector though if anyone has a better idea I am more than willing to listen.
Thanks.
Welcome to Stackoverflow.
For a list that small, it doesn't really matter. Generally though you are going to get better lookup times from ordered collections. I would suggest std::set. If that doesn't work in your scenario (e.g. you want to determine the order yourself) then std::map may be better.
Edit*
As you clarified in your comment, all you want is the index from a vector, then I would do something like this.
int main() {
vector<string> v;
vector<string>::iterator it;
v.push_back("a");
v.push_back("b");
v.push_back("c");
v.push_back("d");
it=find(v.begin(),v.end(),"c");
if(it!=v.end()){
int index = distance(v.begin(), it);
cout<<"found "<<index<<endl;
}
else{
cout<<"not found"<<endl;
}
return 0;
}
Sorry if my formatting is strange, I'm not really a cpp programmer
I'm working on implementing a divide-and-conquer algorithm that finds the two points closest to eachother and the distance between them. My final solution finds the correct answer (compared to using brute force), but about 1/3 of the time would return a segmentation fault error. I've been working on fixing this issue for a couple of days now, adding print statements here and there, but couldn't find the issue. I would appreciate it if someone took a look at my code.
Your "divide" loop assumes that X and Y have the same number of elements. If Y has fewer than X you'll run into undefined behavior, which could be funny results or a crash.
Try changing your access method for the vectors. Vector subscript-operator does seem to have an eratic behaviour in some compilers/platforms (at least from my perspective). I think the problem comes from it being a linked list, resulting in being non-contiguous in memory, but that's just a guess. (EDIT: std::vector are contiguous in memory, the problem is something else) Try using iterators instead (easy-peasy with auto keyword), such as:
for (auto iter = Yprime.begin(); iter != Yprime.end(); iter++) {
// Your code here
}
Hope this works for your case!
I have a group of primitive data types that needed to be kept tracked by some sort of ID.
I am looking for a data structure that would let me keep them tighly packed and the collection needs to grow as needed.
The C++ std::vector would be a perfect solution, except that when it deletes an element, some indices could be changed so I could no longer reference an element by its index. I also thought about map, but it is not tightly packed.
My solution would be rewriting std::vector such as when an entry is deleted, the entry is marked as 'dirty', and when a new entry is needed, it could try to reuse 'dirty' entries and if not possible, simply grow as std::vector.
But my problem appears to be common, and I am sure someone has ran into it. Would I need to reinvent the wheel or could someone point me to another direction?
You could use a vector<boost::optional<T>>.
To delete an item:
vec[val.id] = boost::none; // now this slot is empty
To add an item:
auto it = std::find(vec.begin(), vec.end(), boost::none);
if (it == vec.end()) {
val.id = vec.size(); // or however
vec.push_back(val);
}
else {
// this is an empty slot, overwrite it
val.id = std::distance(vec.begin(), it);
*it = val;
}
To make that more efficient, you can also keep a separate list of free indices when you do erasing - but that's just an optimization on top of the general idea.
Your idea to keep track of deleted ("dirty" in your parlance) entries is commonly done, and referred to as using a "free list."
Another idea would be to simply use the pointer (address of each datum) as the ID.
I'm late the party, but what about swapping the last element with the element to be deleted and popping that element off the back of the vector? Free lists are also a good solution, but might be over engineering it depending on what you're doing.
I'm coding a program where I want to draw a card, and then delete so that it doesn't get drawn again.
I have a vector of Cards (class containing 2 structs that define Suit and Value) called deck and I don't really know how to use iterators very well, here a code snippet:
void Player::discardCard(CardDeck masterDeck)
{
cout << "Erasing: " << masterDeck.getDeck().at(cardSelect).toString() << endl;
/*Attempt1*/
masterDeck.getDeck().erase(masterDeck.getDeck().begin()+cardSelect);
/*Attempt 2*/
vector<Card>::iterator itr;
itr = masterDeck.getDeck().begin() + cardSelect;
masterDeck.getDeck().erase(itr);
}
cardSelect has the location of the card I'm going to delete.
It's generated randomly within the boundaries of 0 and the size of deck; therefore it shouldn't be pointing to a position out of boundaries.
Everytime I compile I get the following error:
"Expression: vector erase iterator outside range"
I really don't know what to do, hopefully someonw can help me, thanks in advance!
My bet is that getDeck returns the vector by value. It causes itr to point to and erase to operate on different copies of the vector. Thus you get the error. You should return the vector by reference. Change getDeck signature to this one:
vector<Card>& getDeck()
Let me go off topic first. Your design is a little suspect. First passing in CardDeck by value is almost certainly not what you want but that's even beside the point. Why should your Player class have all this inside knowledge about the private innards of CardDeck. It shouldn't care that you store the deck as a vector or deque (ha ha), or what the structure is. It just shouldn't know that. All it knows is it wants to discard a card.
masterDeck.Discard(selectedCard);
Also note that selectedCard has to be between 0 and ONE LESS than the size of the deck, but even that's probably not your problem (although it will be 1/53rd of the time)
So to answer your question we really would need to now a little more about masterDeck. Did you implement a valid custom copy constructor? Since you're passing by value odds are good you're not correctly copying the underlying vector, in fact it's probably empty and none of the deletes will work. Try checking the size. If you don't ever want the deck copied then you can let the compiler help you by declaring a private copy constructor and then never defining it. See Scott Meyer's Effective C++ Item 11.
Finally one last piece of advice, I believe once you erase with your iterator, you invalidate it. The vector might get reallocated (almost certainly will if you erase anywhere but the end). I'm just telling you so that you don't try to call erase more than once on the same iterator. One of the tricky things about iterators is how easy it can be to invalidate them, which is why you often seen checks for iter != coll.end().
"It's generated randomly within the boundaries of 0 and the size of deck".
The valid range should be "between 0 and the size of the deck minus 1". This could generate range error at run time.
I have a std::map declared thusly in a legacy MFC application:
typedef std::map<long, CNutrientInfo> NUTRIENT_INFO_MAP;
typedef NUTRIENT_INFO_MAP::const_iterator NUTRIENT_INFO_ITER;
typedef NUTRIENT_INFO_MAP::value_type NUTRIENT_INFO_PAIR;
static NUTRIENT_INFO_MAP m_NutrientInfoMap;
m_NutrientInfoMap is populated when the app loads by looping through a table and creating an instance of CNutrientInfo and then inserting it into the std:map like:
m_NutrientMapInfo.insert(NUTRIENT_INFO_PAIR(nutrient.GetId(), nutrient));
The std::map now contains a list of the nutrients that have been defined by the database. At some point a user can add a new nutrient to this list and it checks to see if what the user is adding already exists in the list. It does that check like:
NUTRIENT_INFO_ITER iter = m_NutrientInfoMap.begin();
while (iter != m_NutrientInfoMap.end())
{
m = (*iter).second;
if (_stricmp(m.GetFullName().c_str(), name.c_str()) == 0)
{
return m;
}
iter++;
}
Or at least it's supposed to. When the function actually gets called it never advances past the initial line of the while loop. Placing a breakpoint there simply shows that the line in question is called over and over and never advances beyond it, which hangs the app. If you step into the actual comparison it compares correctly and then returns to the while loop line. Stepping in again to advance into the body of the loop simply returns to the while loop line. This same logic is used elsewhere in the app with no trouble so I'm stumped as to what's going on in this case. I've re-written the logic above using a for-loop and it works just fine, so it's not like I can't work around it, but C++ isn't my strongest language and as this is a legacy app that I'm trying to help support, I'd really like to learn and understand WHY this is doing what's it's doing for future reference. Plus, since the logic works elsewhere and not here perhaps there's an underlying cause that is what actually needs to be addressed.
Any suggestions or thoughts on this would be greatly appreciated.
Thanks in advance.
Is your example actually pasted from the source? Maybe it looks more like:
while (iter != m_NutrientInfoMap.end()); // <== note the semi-colon
{
m = (*iter).second;
if (_stricmp(m.GetFullName().c_str(), name.c_str()) == 0)
{
return m;
}
iter++;
}
There is nothing in the code posted above which can cause that behavior. Are you sure that you are incrementing some other iterator inside the loop or may be there two iterators with the same name (one inside the loop) with different scopes and you are incrementing the wrong iterator ? If this is not the case, then only other alternative I could see is to note down the value of m_NutrientInfoMap.end() and check why ++iter is not evaluating into that value.