Getting array boundary exception that is so weird

Started by
17 comments, last by ankhd 9 years, 2 months ago

I'm trying to implement a space invader game, now I'm trying to check if bullets hit an invader, then I remove it.

I'm ONLY get an exception when I try to shoot the last invader in the row. For e.g If I have just one row and 8 invaders, the last one, when the bullet hits it, I got a strange boundary out of exception.

The initialization is like that, in pseudo code:


struct Invaders_t
{
vector<Invader*> *invaders;
}
for (int j = 0; j < 1; j++)
{
x_spacing = 15;
for (int i = 0; i < 8; i++)
{
Invader *invader = new Invader();
g_Invaders.invaders->push_back(invader);

}

for each frame:


for (int x = 0; x < 1; x++)
{
for (int y = 0; y < 8; y++)
{
Invader *pInvader = (*(g_Invaders.invaders))[x*8 + y];

if (pInvader != nullptr && pInvader->AlienAtals->GetImage() != nullptr)
{

for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;

if (CheckCollision(left, top, pInvader->AlienSprite->m_X, pInvader->AlienSprite->GetImage()->GetWidth()/4
, pInvader->AlienSprite->m_Y, pInvader->AlienSprite->GetImage()->GetHeight()/4))
{
std::vector<Invader*>::iterator iter = g_Invaders.invaders->begin();
std::advance(iter, (x *8) + y);
Invader *invader = *iter;
g_Invaders.invaders->erase(iter);
delete invader;
}

}
}
}
}
Advertisement

It probably happens on the next frame, and it's because of this line:
g_Invaders.invaders->erase(iter);

You erase the last element perfectly fine, so now your g_Invaders.invaders->size() == 7, and the last allowed index is 6. On the next frame, you call Invader *pInvader = (*(g_Invaders.invaders))[x*8 + y];, which will attempt to access the element at index 7.

If you want to fix it, either don't erase the element from the vector, and instead do *iter = nullptr;, or change your outermost loops to use iterators. Since you're not using the x and y loop variables for nothing else except to calculate which element to access, iterators are the logical choice, but since it's your code, do what makes you most comfortable.

devstropo.blogspot.com - Random stuff about my gamedev hobby

Also worth pointing out if you change to using iterators you can't modify an stl container whilst iterating it, e.g. By inserting or deleting entries, unless you take special measures with your loop.

If you really need to, You should do something like:


for (iter i = container.begin(); i != container.end();)
{
        If (deleting)
                  i = container.erase(i);
        else
                 ++i;
}
You have to choose between a static or dynamic container here. You're indexing like you have a 2-D grid of objects, but you're erasing elements like you have a dynamic array.

Either switch to an actual 2-D grid (e.g. array with nullable elements), or iterate over the vector's current elements, not the grid of elements you originally created.

Would someone please show me the fix in code?

Just want to learn.

In case I just want to iterate over vectors element ?

@rip-off, what did you mean by array that has nullable elements ?

I changed the code, but I still get exception while shooting the last element


for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); invIter++)
{
 
for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;


if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
invIter = g_Invaders.invaders->erase(invIter);
  
}
 
}
}

Also worth pointing out if you change to using iterators you can't modify an stl container whilst iterating it, e.g. By inserting or deleting entries, unless you take special measures with your loop.

If you really need to, You should do something like:


for (iter i = container.begin(); i != container.end();)
{
        If (deleting)
                  i = container.erase(i);
        else
                 ++i;
}

You need to do it like this. Note how the increment is not done in the for loop... thing, but in the else clause. You get the new value of i EITHER by incrementing (++i), OR from the erase method (because you modified the vector and now the iterator held in i is somehow not valid so you cant just increment it).

o3o

I changed the code, but I still get exception while shooting the last element
[snip code]


Here you get the exception because when you reach the last element, you erase it properly, and that makes the invIter iterator invalid. Right after that you try calling invIter++, which is what causes your error.
Do what braindigitalis did.

devstropo.blogspot.com - Random stuff about my gamedev hobby

Also this won't fix your bug, but this is important in my opinion for readability. Instead of writing vector<Invader*>::iterator everywhere, do this in a header file:


typedef std::vector<Invader*> InvaderVec
typedef InvaderVec::iterator InvaderVecIter

This means you can use the shorter, more readable InvaderVec and InvaderVecIter instead to declare loops etc.

?...and here endeth the lesson smile.png

Thanks.

I have done the following, but the game goes into forever loop


for (vector<Invader*>::iterator invIter = g_Invaders.invaders->begin(); invIter != g_Invaders.invaders->end(); )
{

for (list<Bullet*>::iterator it = ship->Bullets->begin(); it != ship->Bullets->end(); it++)
{
float left = (*it)->Position.x - 5;
float top = (*it)->Position.y - 13;


if (CheckCollision(left, top, (*invIter)->AlienSprite->m_X, (*invIter)->AlienSprite->GetImage()->GetWidth() / 4
, (*invIter)->AlienSprite->m_Y, (*invIter)->AlienSprite->GetImage()->GetHeight() / 4))
{
Iw2DSetColour(0xff0000ff); // Set red
Iw2DDrawRect(CIwFVec2(left, top), CIwFVec2((*invIter)->AlienSprite->m_W, (*invIter)->AlienSprite->m_H)); // Draw red outline
invIter = g_Invaders.invaders->erase(invIter);

}
else
{
invIter++;
}

}
}

This topic is closed to new replies.

Advertisement