Question

I'm working on a game and I'm currently working on the part that handles input. Three classes are involved here, there's the ProjectInstance class which starts the level and stuff, there's a GameController which will handle the input, and a PlayerEntity which will be influenced by the controls as determined by the GameController. Upon starting the level the ProjectInstance creates the GameController, and it will call its EvaluateControls method in the Step method, which is called inside the game loop. The EvaluateControls method looks a bit like this:

void CGameController::EvaluateControls(CInputBindings *pib) {
    // if no player yet
    if (gc_ppePlayer == NULL) {
        // create it
        Handle<CPlayerEntityProperties> hep = memNew(CPlayerEntityProperties);
        gc_ppePlayer = (CPlayerEntity *)hep->SpawnEntity();
        memDelete((CPlayerEntityProperties *)hep);
        ASSERT(gc_ppePlayer != NULL);
        return;
    }

    // handles controls here
}

This function is called correctly and the assert never triggers. However, every time this function is called, gc_ppePlayer is set to NULL. As you can see it's not a local variable going out of scope. The only place gc_ppePlayer can be set to NULL is in the constructor or possibly in the destructor, neither of which are being called in between the calls to EvaluateControls. When debugging, gc_ppePlayer receives a correct and expected value before the return. When I press F10 one more time and the cursor is at the closing brace, the value changes to 0xffffffff. I'm at a loss here, how can this happen? Anyone?

Was it helpful?

Solution

Are you debugging a Release or Debug configuration? In release build configuration, what you see in the debugger isn't always true. Optimisations are made, and this can make the watch window show quirky values like you are seeing.

Are you actually seeing the ASSERT triggering? ASSERTs are normally compiled out of Release builds, so I'm guessing you are debugging a release build which is why the ASSERT isn't causing the application to terminate.

I would recommend build a Debug version of the software, and then seeing if gc_ppePlayer is really NULL. If it really is, maybe you are seeing memory heap corruption of some sort where this pointer is being overridden. But if it was memory corruption, it would generally be much less deterministic than you are describing.

As an aside, using global pointer values like this is generally considered bad practice. See if you can replace this with a singleton class if it is truly a single object and needs to be globally accessible.

OTHER TIPS

set a watch point on gc_ppePlayer == NULL when the value of that expression changes (to NULL or from NULL) the debugger will point you to exactly where it happened.

Try that and see what happens. Look for unterminated strings or mempcy copying into memory that is too small etc ... usually that is the cause of the problem of global/stack variables being overwritten randomly.

To add a watchpoint in VS2005 (instructions by brone)

  1. Go to Breakpoints window
  2. Click New,
  3. Click Data breakpoint. Enter
  4. &gc_ppePlayer in Address box, leave other values alone.
  5. Then run.

When gc_ppePlayer changes, breakpoint will be hit. – brone

My first thought is to say that SpawnEntity() is returning a pointer to an internal member that is getting "cleared" when memDelete() is called. It's not clear to me when the pointer is set to 0xffffffff, but if it occurs during the call to memDelete(), then this explains why your ASSERT is not firing - 0xffffffff is not the same as NULL.

How long has it been since you've rebuilt the entire code base? I've seen memory problems like this every now and again that are cleared up by simply rebuilding the entire solution.

Have you tried doing a step into (F11) instead of the step over (F10) at the end of the function? Although your example doesn't show any local variables, perhaps you left some out for the sake of simplicity. If so, F11 will (hopefully) step into the destructors for any of those variables, allowing you to see if one of them is causing the problem.

You have a "fandango on core."

The dynamic initialization is overwriting assorted bits (sic) of memory.

Either directly, or indirectly, the global is being overwritten. where is the global in memory relative to the heap?

binary chop the dynamically initialized portion until the problem goes away. (comment out half at a time, recursively)

Depending on what platform you are on there are tools (free or paid) that can quickly figure out this sort of memory issue.

Off the top of my head:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top