调试了一个crash,很诡异,程序是崩在一个线程函数中调用一个对象的虚函数——vptr被改了。
Review了这个对象所有引用的代码没能发现问题,只能操起windbg调试了,好在这个问题比较容易重现。调试中发现这个对象被正确的构造了之后一直没有释放,却在另一个模块中通过new申请到了同一块内存,因此造成vptr等内容被修改,线程函数中再调用其虚函数就崩溃了。
通过ba r 4 0xXXXXXXXX对这块内存下了个读断点,几次断下之后终于找到了罪魁祸首——一个函数内一个临时的指针变量delete掉了这块内存。
这不科学!这只是一个函数内的一个临时的指针变量,初始化为NULL,甚至逻辑都没有走到对它进行new赋值的地方,怎么会突然有了值,并且delete掉一个不相干的对象?!伪代码如下:
if(!DoSomething()) // 这里返回的是FALSE, 因此没走到new对pData赋值.
{
goto Exit;
}
BYTE* pData = NULL;
…
pData = new BYTE[1024];
…
Exit:
if(pData != NULL)
{
delete [] pData;
pData = NULL;
}
但是事实如此,反复review这段代码发现最有可能出现问题的地方在这处goto。通过编写demo发现VS 2005对于goto后面才定义的变量只会在函数进入前分配栈空间,要在执行到goto后面的赋值语句时才会对变量进行赋值。这也就让delete [] pData却释放了别处申请的内存成为了可能,因为指不定pData的栈内容是什么呢。
注意应尽量避免用goto,如果一定要使用保证函数进入就定义所有的变量并赋初值。