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

Posted

技术标签:

【中文标题】为啥即使在调试版本中,VS 和 Windbg 也会将“this”指针打印为“0xcccccccc”?【英文标题】:Why does VS and Windbg print "this" pointer as "0xcccccccc" even in debug version?为什么即使在调试版本中,VS 和 Windbg 也会将“this”指针打印为“0xcccccccc”? 【发布时间】:2017-01-12 14:32:37 【问题描述】:

我尝试在进入成员函数时使用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调试版(32bit)编译的

(2) 我在“DoIt”函数中设置了断点。

(3)在点击Base->FuncOne()时,我按“F11”进入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调试,同样的观察。但为什么呢?

【问题讨论】:

this 未在 Doit 函数中定义:它不是任何类的成员。 如果您将return 1 放在单独的行上,您将在那里看到一个有效的this 就像 RustyX 说的,你不能进入一个单行函数,除非有一个函数调用堆栈。至少在 Visual Studio 中没有。将语句放在单独的行中。 【参考方案1】:
virtual int FuncOne(void)  return 1; ;//set break point here.

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

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

这样写就不会有这个问题了:

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

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

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

【讨论】:

太搞笑了。 :) 但是为什么“步入”不进入 FuncOne 并停在return 1;?? 调试信息是基于行号的。 VS 支持的其他语言也可以考虑列号。由于预处理器,这在 C 和 C++ 中被破坏了,编译器只有在代码被破坏后才能看到代码。哦,那该死的预处理器。顺便说一句,没那么有趣。 这是一行,因为他们认为它没有“做任何值得调试的事情”——但人们经常调试人们认为错误的地方。有时step into expression(或statement)会很方便! 太糟糕了,不可能是假的。太棒了。

以上是关于为啥即使在调试版本中,VS 和 Windbg 也会将“this”指针打印为“0xcccccccc”?的主要内容,如果未能解决你的问题,请参考以下文章

windebug

如何使用WinDbg调试进程信息

已安装 VS 2015 时如何安装 WinDbg?

windbg怎么打开pdb文件

windbg断点命中时,其它线程在运行吗

VS2012中使用SOS调试CLR