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

Why does VS and Windbg print "this" pointer as "0xcccccccc" even in debug version?

我尝试使用 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) I compiled it with VC2015 debug version(32bit)

(2) I set the break point in "DoIt" function.

(3) When hit Base->FuncOne(), I pressed "F11" to enter the function of DerivedOne.

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

0:000> k
 # ChildEBP RetAddr  
00 0041f654 0022157c ConsoleApplication1!DerivedOne::FuncOne [d:\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1.cpp @ 13]
01 0041f734 0022173c ConsoleApplication1!DoIt+0x2c [d:\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1.cpp @ 23]
02 0041f850 00221dc9 ConsoleApplication1!main+0x7c [d:\documents\visual studio 2013\projects\consoleapplication1\consoleapplication1.cpp @ 36]
03 0041f8a0 00221fbd ConsoleApplication1!__tmainCRTStartup+0x199 [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.c @ 626]
04 0041f8a8 75b9338a ConsoleApplication1!mainCRTStartup+0xd [f:\dd\vctools\crt\crtw32\dllstuff\crtexe.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 参数就是其中之一,它作为函数的第一个参数传递。

您只能在序言代码执行后观察到 this 具有正确的值。这需要使用 Debug > Windows > Disassembly,这样你就可以跳过序言代码,一直到 mov dword ptr [this],ecx 之后的指令。很别扭。

这样写就不会出现这个问题了:

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

或者您喜欢的任何牙套样式。现在设置断点确保函数的序言被执行并且 this 具有预期值。

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