[置顶] 一种异常解析-HeapValidate异常

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)

参考文档:

http://blog.csdn.net/hgy413/article/details/6716397

http://blog.csdn.net/lbhli/article/details/6797145


前注:

删除类的编译翻译,例如delete pItem; 会翻译成

1. pItem::~析构函数();       // 调析构

2. operator delete(pItem); // 调类对象所占内存清理(大小sizeof(类))


如果pItem的类的析构为虚函数时,翻译成 

1. 调用虚函数表中的析构函数,一层一层调用析构函数

2. 重设删除地址 (猜测,基于上一步指向虚函数的实际类型,对指针重设:类似 CChlid继承自CItem, CChild* pChild = (CChild*)pItem,从而指针与CItem指针偏移差考虑进来) 

3. operator delete(删除地址);


1. 父类无虚构函数,子类有虚构函数,使用父类delete时崩溃

class IBase
{
public:
};

class CItem1 : public IBase
{
public:
    virtual void A(){}
};
int main()
{
   IBase* pBase = CItem1();
   delete pBase;
}


添加调测代码,查输出的话,可以看到父类指向的地址偏移了四位(虚函数表的空间)

输出:

pItem=0x5d0610, sizeof(*pItem)=4
pBase=0x5d0614, sizeof(*pBase)=1


#define ViewPointerAddress(pVal) ViewPointerAddressFunc(#pVal, pVal)
template <typename T>
void ViewPointerAddressFunc(char* pCharName, T* pPointer)
{
    char sLog[256];
    sprintf_s(sLog, "%s=0x%x, sizeof(*%s)=%d\n", pCharName, pPointer, pCharName, sizeof(*pPointer));
    printf(sLog);
    OutputDebugStringA(sLog);
}
...
    ViewPointerAddress(pItem);
    ViewPointerAddress(pBase);
...


报错在HeapValide异常这一块,为什么会报错呢:

进一步按照debug的方法,添加调试代码,定位可以看到:

    _CrtMemBlockHeader *pBaseHdr = pHdr(pBase);
    _CrtMemBlockHeader *pItemHdr = pHdr(pItem);
    HeapValidate(GetProcessHeap(), 0, pHdr(pItem));
    HeapValidate(GetProcessHeap(), 0, pHdr(pBase));


报错在 HeapValidate(GetProcessHeap(), 0, pHdr(pBase)) 这句话上,从结构上看:pBaseHdr中是有些异常的



分析:

每个内存分配前面都放有一个 _CrtMemBlockHeader 结构,但像上面的情况,由于pBase的地址偏移了4位,导致获取它前面的 _CrtMemBlockHeader 存在异常

所以HeapValidate鉴权失败,崩溃退出



2. 多重继承时的问题:

多重继承虚函数时,也有类似的问题,例如:

class A{ public: virtual void f1()=0;};

class B{ public: virtual void f2()=0;};

class C : public A, public B {}

C* pC = new C();

B* pB = pC;

delete pB;


此时如果新建C对象,使用B指向他的时候,地址会偏移4字节,delete使用B指针的话导致异常;使用A指针的话,由于编译器把他放在前面,它的问题不大,不会崩溃。

pItem=0x4a02f0, sizeof(*pItem)=8
pBaseA=0x4a02f0, sizeof(*pBaseA)=4
pBaseB=0x4a02f4, sizeof(*pBaseB)=4


其实对于多重继承,有没有虚函数,使用非第一个继承类删除都会有问题,例如:

class A{ public: int a;};

class B{ public: int b;};

class C : public A, public B {int c;}

C* pC = new C();

B* pB = pC;

delete pB;

查看指针的话,会看到内存分布:A|B|c-内部变量

(转成A的时候,删除没有问题;转成B删除就会有问题)


(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu)






你可能感兴趣的:([置顶] 一种异常解析-HeapValidate异常)