为什么即使在调试版本中,VS 和 Windbg 也会"this"指针打印为"0xcccccccc"?



我试图在输入成员函数时使用windbg打印"this"指针,如下所示。

class IBase {
    int m_i;
public:
    IBase() :m_i(23) {}
    virtual int FuncOne(void) = 0;
    virtual int FuncTwo(void) = 0;
};
class DerivedOne : public IBase {
public:
    virtual int FuncOne(void) { return 1; };//set break point here.
    virtual int FuncTwo(void) { return 2; };
};
class DerivedTwo : public IBase {
public:
    virtual int FuncOne(void) { return 101; };
    virtual int FuncTwo(void) { return 102; };
};
void DoIt(IBase* Base)
{
    int i=Base->FuncOne();//break point here
}
int main(int argc, char *argv[])
{
    DerivedOne d1;
    DerivedTwo d2;
    DoIt(&d1);
    DoIt(&d2);
    return 0;
}

(1( 我用VC2015调试版本(32位(编译了它

(2( 我在"DoIt"函数中设置了断点。

(3( 当点击Base->FuncOne((时,我按下"F11"进入DerivedOne的函数。

现在我可以看到调用堆栈是这样的:

0:000> k
 # ChildEBP RetAddr  
00 0041f654 0022157c ConsoleApplication1!DerivedOne::FuncOne [d:documentsvisual studio 2013projectsconsoleapplication1consoleapplication1.cpp @ 13]
01 0041f734 0022173c ConsoleApplication1!DoIt+0x2c [d:documentsvisual studio 2013projectsconsoleapplication1consoleapplication1.cpp @ 23]
02 0041f850 00221dc9 ConsoleApplication1!main+0x7c [d:documentsvisual studio 2013projectsconsoleapplication1consoleapplication1.cpp @ 36]
03 0041f8a0 00221fbd ConsoleApplication1!__tmainCRTStartup+0x199 [f:ddvctoolscrtcrtw32dllstuffcrtexe.c @ 626]
04 0041f8a8 75b9338a ConsoleApplication1!mainCRTStartup+0xd [f:ddvctoolscrtcrtw32dllstuffcrtexe.c @ 466]
05 0041f8b4 77529902 kernel32!BaseThreadInitThunk+0xe
06 0041f8f4 775298d5 ntdll!__RtlUserThreadStart+0x70
07 0041f90c 00000000 ntdll!_RtlUserThreadStart+0x1b

但是"dv"命令给出了意想不到的结果

0:000> dv
       this = 0xcccccccc

为什么会这样?程序运行良好,调试版本没有优化任何内容,似乎一切都很好。但是为什么"this"指针无效呢?

我用VC自己的IDE来调试,同样的观察。但为什么呢?

virtual int FuncOne(void) { return 1; };//set break point here.

导致这个问题的是您的编码风格。由于您在函数定义的同一行中编写了函数体,因此断点设置在函数的开头,而不是函数体的开头。此时,函数的序言尚未执行。设置堆栈框架并检索函数参数的代码。隐藏的this参数就是其中之一,它作为函数的第一个参数传递。

只有在执行了序言代码之后,才能观察到这个具有正确的值。这需要使用"调试">"Windows">"反汇编",这样您就可以跳过序言代码,一直到mov dword ptr [this],ecx之后的指令。非常尴尬。

当它这样写的时候,你不会有这个问题:

virtual int FuncOne(void)
{ return 1; };//set break point here.

或者你喜欢的任何支架样式。现在设置断点可以确保函数的序言被执行,并且this具有预期值。

或者通过知道遍历函数并不有趣来解决它,因为它不会做任何值得调试的事情。你这样写的基本原因。请改用"调试">"单步执行"。如果您不小心进入了这样一个函数,请使用Debug>Step Out快速返回到您实际想要调试的代码中。

相关内容

最新更新