在使用Visual Studio 2012 RC调试某些C ++代码时,我注意到一个奇怪的行为,它在类成员函数和成员变量值上有断点。
当我在类成员函数上设置断点时,VS 2012将断点放在函数花括号上。现在,当将鼠标悬停在函数使用的成员变量上时,该值始终为“未初始化”。但是,按F10进入下一行(函数中的第一行),成员变量现在更改为正确的值。
这看起来在步进成员函数的第一行之前,不会加载类的成员变量的值。但这非常令人困惑 - 那么打破大括号的重点是什么呢? (这是在函数上设置断点时的默认值。)
打破班级成员职能:
成员变量active
据说是true
(实际上并非如此!)
使用F10步入下一行:
成员变量active
现在被称为false
(这是正确的。)
我在这里遗漏了什么,或者这是Visual Studio 2012 RC中的实际错误?
编辑:我找到了我的Visual Studio 2010副本并试了一下。关于断点的行为是相同的。不同之处在于IntelliSense如何处理这种情况。在VS 2010中,当打破大括号时,IntelliSense根本不会弹出工具提示,而在VS 2012中,工具提示始终显示。我认为VS 2010的行为要好得多,以避免混淆。
答案 0 :(得分:1)
默认情况下,在x86上,C ++成员函数使用thiscall调用约定(*),它通过this
寄存器传递ecx
指针。但是,该函数可以使用该寄存器进行计算,因此调试器在函数执行的整个时间内不能依赖其值为this
指针。
因此,在未经优化的构建中,作为在输入函数体之前执行的函数序言的一部分,this
指针将“溢出”到堆栈:它被复制到已知的偏移量在堆栈上,以便调试器可以可靠地获取其值。它是调用程序在您查看this
或成员变量(通过this
指针隐式访问时)时使用的this
指针的副本。
在{
的左大括号上放置断点时,断点位于函数序言的初始地址,即调用函数时将执行的第一条指令。只有在您跨过此左大括号后,函数序言才会执行并且this
指针溢出到堆栈。
如果您需要通过函数序言进行调试,这将非常有用。如果您单步执行反汇编(Debug - > Windows - > Disassembly),则会更加明显。
(*)在x64上,只有一个调用约定,它是一个fastcall调用约定。 this
指针最终将通过rcx
寄存器传递,因为它是函数的“第一个”参数。
在x86上,并非所有成员函数都使用thiscall调用约定,它只是默认值。与其他函数类型一样,您可以指定函数的调用约定。例如,对于使用stdcall调用约定的COM组件,这样做了。
答案 1 :(得分:1)
通过在花括号上设置断点,您可以让调试器在摇滚和硬地之间做出选择。一个难点是编码风格惯例,你喜欢K& R支撑吗?
void foo() {
// etc..
}
你如何在那个断点上设置断点?调试器在安全方面出错,它在函数入口点设置断点。当你的断点命中时,你可以使用Debug + Windows + Assembly,你会发现它在函数的第一个机器代码指令上设置了断点。几乎总是push ebp
。实际上与调试器试图处理模糊断点的正常方式有点不同,它通常向前看,而不是向后看。所以这是非常有意义的。在托管代码的调试器中解决的问题不仅仅是基于行的,而是关注列。这没有流回C ++调试器,它仍然是基于行的。
因此检查局部变量不会很好,没有。直到函数序言被执行才设置堆栈帧。包括 this 。
调试器向后看而不是向前看的可能原因是你想要单步执行作为局部变量的类对象的构造函数。同样,除了自己倒回堆栈并在构造函数上设置断点之外,你不能设置一个明确的断点。谁知道在哪里。
功能,而不是错误。显而易见的解决方法是在函数体中的第一个语句上设置断点。一切都是由那时设置的,堆栈框架和局部变量。