I created a snake game using objects of snake segments that act together in a linked list type way (I think.) and I notice that when you select quit after you die with more than 2 links, the program throws some run time error. Even funkier, when I used a cout statement to display the addresses of the items I was deleting in the destructor to make sure I was deleting everything, after they were all deleted, and when main returns, it THEN displays the last few addresses and throws the same error. I am confused out of my mind.

I am not going to post the code here as it is multiple files and over 400 lines, but if you actually feel you can help me, I can zip it and post it somewhere if you ask.

Thanks.

I almost forgot to mention, this code is platform specific (windows) and I run on windows XP if you need that info.

Recommended Answers

All 10 Replies

Sounds like you're trying to delete some memory you already deleted, or that someone else deleted for you (like if you were using an STL container class to hold the number of links)

I am using my own class and pointers. But I thought it had something to do with the data in memory as well, but the thing that gets me is.
If I restart my game, and then die again after already dying with a ton of links, it doesn't error. As a quick fix for now I just call the code i use to setup a new game right before the return and it fixes it.

We need to see code in order to figure out what's happening. Isolate the problem by creating a small version of the program that exhibits the same problem.

Copy your program and start removing sections of code you know does not cause the problem -- piece by piece. When you get it to a few lines, one of two things will happen:
1) you'll see the problem
2) the code's small enough to post

I believe this is the problem site, but I am not sure because my code is so entwined with itself.

When looking at this problem, keep in mind that the problem doesn't occur if there are two or one links, which means the regular destructor is probably working, just not the if line in the destructor if the condition is met.

// kill is called by the first link of the snake, the first link is never deleted as it is not
// made dynamically
void SnakeSeg::kill()
{
    if (alive)
    {
        alive = false;
        erase();
        links = 0;
        if (next)
            delete next; //This code actually works if there is a second link, calls the next dynamically made snake segment's constructor.
        
    }
}

SnakeSeg::~SnakeSeg()
{
    putxy(x,y,'X');
    Sleep(50);
    erase();
    if (next) // This if statement is ONLY called if there if there is a third link problem here?
        delete next;
}

Also, the constructor DOES run for all snakes, but right when the return statement is encountered in main (even after all snake segments are destroyed and it asks the user if he/she wants to continue or not) then I get the error.

And since this following code runs flawlessly while moving the snake around, there must not be a dangling pointer.

else if (next) // Runs only if it is has a proceeding  and succeeding segment, meaning 3 or more
    {
        lastx = x;
        lasty = y;
        x = previous->lastx;
        y = previous->lasty;
        dir = previous->dir;
        next->move();
    }

EDIT:
Found the real problem site after a bit of testing, it is erase() that is called in the deconstructor, although I don't know what is wrong with the erase function (I know this is it because i commented it in the deconstructor and no more bug.) Any clue on why erase is causing this bug?

void SnakeSeg::erase()
{
    field[x][y] = 0; //field is a static member.
    putxy(x, y, ' '); // this code actually works to erase the snake when it is dead (when it goes into the deconstructor)
}
void SnakeSeg::kill()
{
    if (alive)
    {
        alive = false;
        erase();
        links = 0;
        if (next)
        {
            delete next; 
            next = 0 ; // isn't this required?
        }
    }
}

No, as that is set when the snake is revived for a new game, and kill() is only called once for each destruction of the snake. I have already isolated the problem if you look at my edit in the last post.

void SnakeSeg::erase()
{
    field[x][y] = 0; //field is a static member.
    putxy(x, y, ' '); // this code actually works to erase the snake when it is dead (when it goes into the deconstructor)
}

can cause such an error if and only if values of x and/or y are out of range.
print out the values of x, y in your destructor (or assert their validity) and you would discover that they are invalid. check the place where you had updated x, y for the last time before calling the destructor. and if the field array was dynamically allocated, that it has not been deleted before this.
i would be surprised if you did not have a dangling pointer. it would be a good idea to assert that x and y are in range every time you update them.

Even further isolated the problem, in erase I added:

if (x < 0 || x > XMAX+1)
        cout << x;
    if (y < 0 || y > YMAX+1)
        cout << y;

and commented out the field[x][y] = 0;
It turns out it is over-indexing somehow. Is this because the destructor was already called so these values were already set to 0? I not sure how to fix this.

but even if I try calling erase conditionally in the destructor

if (x >= 0 && x <= (XMAX + 1) && y >= 0 && y <= (YMAX + 1))
    {
        erase();
    }

if causes it to error with the same over indexing, which blows my mind.

it is impossible to say just by looking at the fragments you posted. this one seems interesting.
> since this following code runs flawlessly while moving the snake around, there must not be a dangling pointer.

else if (next) // Runs only if it is has a proceeding  and succeeding segment, meaning 3 or more
    {
        lastx = x;
        lasty = y;
        x = previous->lastx;
        y = previous->lasty;
        dir = previous->dir;
        next->move();
    }

are you absolutely certain that this will never be called after SnakeSeg::kill has been called? you would be able to move the snake around flawlessly (kill has not been called); but by the time you reach the destructor things might have got out of hand.

in any case, do yourself a favour:
except in the destructor where you are deleting a member, never write delete ptr ; without also writing ptr = 0 ; immediately after that.
better still: completely avoid using c-style pointers whenever possible . prefer things like std::vector<>, std::auto_ptr<>, std::string, std::tr1::shared_pointer<> etc.

commented: Solved my problem, thanks for the help +1

Thanks, I set them to 0 there like you suggested, it fixed the problem. I don't see how though.

As in my main loop, I had:

        while(snake.isAlive())
        {
            snake.move();

and it is the only place move is not called by itself.

and if it is called by itself and calls kill because it ran into an obstacle, it returns to prevent any other pointer to be dereferenced and call move for.

        kill();
        return;

I guess I'll mark this solved, but this is confusing lol.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.