0

在使用 Visual Studio 2012 RC 调试一些 C++ 代码时,我注意到在类成员函数和成员变量值上有断点的奇怪行为。

当我在类成员函数上设置断点时,VS 2012 将断点放在函数大括号上。现在,当悬停在函数使用的成员变量上时,该值始终“未初始化”。但是,按 F10 进入下一行(函数中的第一行),成员变量现在更改为正确的值。

这看起来像一个类的成员变量的值直到进入成员函数的第一行才被加载。但这很令人困惑——那么打破大括号的意义何在?(在函数上设置断点时是默认设置。)

打破类成员函数:

支架断裂

据说成员变量activetrue(实际上不是!)

使用 F10 进入下一行:

在此处输入图像描述

现在说成员变量activefalse(这是正确的。)

我在这里遗漏了什么还是这是 Visual Studio 2012 RC 中的实际错误?

编辑:我找到了我的 Visual Studio 2010 副本并进行了尝试。关于断点的行为是相同的。不同之处在于 IntelliSense 如何处理这种情况。在 VS 2010 中,IntelliSense 在大括号上断开时根本不会弹出工具提示,而在 VS 2012 中始终显示工具提示。我认为 VS 2010 的行为避免混淆要好得多。

4

2 回答 2

1

默认情况下,在 x86 上,C++ 成员函数使用 thiscall 调用约定(*),它通过寄存器传递this指针ecx。但是,该函数可以使用该寄存器进行计算,因此调试器不能this在函数执行的整个过程中将其值作为指针。

因此,在未优化的构建中,作为在进入函数体之前执行的函数序言的一部分,this指针将“溢出”到堆栈:它被复制到堆栈上的已知偏移量,以便调试器可以可靠地获得它的价值。它是this调试器在您监视时使用的指针的副本this或成员变量(通过this指针隐式访问)。

当您在 的左大括号上放置断点时{,断点将放置在函数序言的初始地址处,即在调用函数时将执行的第一条指令处。只有在您跨过这个左大括号之后,函数序言才会执行,并且this指针会溢出到堆栈中。

如果您需要通过函数序言进行调试,这将很有用。如果您逐步进行反汇编(调试-> Windows-> 反汇编),那么会发生什么情况会更加明显。


(*) 在 x64 上,只有一个调用约定,它是一个 fastcall 调用约定。指针最终将this通过rcx寄存器传递,因为它是函数的“第一个”参数。

在 x86 上,并非所有成员函数都使用 thiscall 调用约定,它只是默认值。与其他函数类型一样,您可以指定函数的调用约定。例如,对使用 stdcall 调用约定的 COM 组件执行此操作。

于 2012-08-05T21:10:41.087 回答
1

通过在花括号上设置断点,您可以让调试器在硬地和硬地之间进行选择。一个难点是编码风格约定,你喜欢 K&R 支撑吗?

void foo() {
   // etc..
}

你如何在那个上设置断点?出于安全考虑,调试器会出错,它会在函数入口点设置断点。当您的断点命中时,您可以使用 Debug + Windows + Assembly,您会发现它在函数中的第一条机器代码指令上设置了断点。几乎总是push ebp。这实际上与调试器尝试处理模糊断点的正常方式有点不同,它通常向前看,而不是向后看。所以这是非常刻意的。在托管代码的调试器中解决的问题不仅是基于行的,而且还关注列。这并没有流回 C++ 调试器,它仍然是基于行的。

所以检查局部变量不会很好,没有。直到执行设置堆栈帧的函数序言。包括这个

调试器向后看而不是像通常那样向前看的可能原因是您希望单步执行作为局部变量的类对象的构造函数。同样,除了自己倒回堆栈并在构造函数上设置断点之外,您无法设置明确的断点。谁知道在哪里。

功能,而不是错误。显而易见的解决方法是在函数体的第一条语句上设置断点。到那时一切都设置好了,堆栈框架以及局部变量。

于 2012-08-05T21:48:03.143 回答