A.R. A.R. - 8 months ago 58
C++ Question

Bizzare nullref in C++

I have a small piece of C++ code that is making me insane. Whenever it runs, it throws a null reference exception in a very unexpected place.

void CSoundHandle::SetTarget(CSound* sound)
assert(_Target == nullptr);
if (sound == nullptr) { return; }

_Target = sound;

// This works just fine.

// This is the code that throws the exception. It doesn't seem possible, as
// we should not be able to get here if 'sound' is null.

So what the heck is going on? The message in the output window is:

this->_Target-> was nullptr.
0xC0000005: Access violation reading location 0x00000014

I have confirmed in disassembly that it is not taking place inside of the Stop function as well. How is this possible?

The pointer for sound is indeed initialized, and 'this' and 'this->Target' are non-null.

I have somehow solved the problem by slightly changing the declaration of the Stop function:

// From this.
virtual void Stop();

// To this.
void Stop();

This seems especially odd since Play() is also virtual, but works without any trouble. I can't say I've ever seen anything like this before. There are no other functions named 'Stop' in the rest of the program, nor are there subclasses of CSound, so I'm a bit confused.

Answer Source

Reading location 0x00000014 implies you're trying to access a field located 0x14 bytes from the beginning of an object. The pointer to that object is set to null. So the problem is in the caller of your function: it passes a bad pointer to sound, which is neither null nor valid. This is why the null check in your function passes (0x14 isn't null), but you still crash.

Update: The second edit to the question indicates the problem is in calling a virtual function. The null here is the virtual pointer then, and 0x14 is the offset of the Stop virtual function. The virtual pointer is set (in code generated by the compiler) during object construction, and should never point to 0. If it does, some part of the program is corrupting the object. An easy to detect case would be an attempt to reset the object, but memory corruptions (e.g., out-of-bound write to an array) could also cause this issue.