16

我正在尝试调试一个问题,其中一个可执行文件在直接从 Visual Studio 执行时会产生可重复的输出(我想要),但在从命令提示符执行时不会产生可重复的输出。这是一个单线程应用程序,因此在时间方面不应该有任何奇怪的行为。

有人可以列举这两种环境之间可能存在的差异吗?

我确定实际的可执行文件是相同的——它们都是发布版本并且运行相同的 .exe 文件。

以下是环境和结果:

  1. 直接从命令提示符 (cmd) 运行:不可重复的输出
  2. 通过调试 (F5) 从 Visual Studio 运行:可重复输出
  3. 从 Visual Studio 运行而不进行调试 (Ctrl-F5):不可重复的输出

我知道工作目录可能不同,但我手动调整它以确保工作目录相同。

基于这些结果,看起来运行“带调试”(即使在发布版本中)以某种方式解决了问题。这是否指向一个可能的罪魁祸首?运行带有调试和不带调试的可执行文件有什么区别?

解决方案:正如接受的答案中指出的那样,调试堆是问题所在。问题是在我们代码的深处,有人在初始化之前访问了一个大数组的一部分。他们已经用 malloc 分配了内存并且没有将内存初始化为 0。调试堆(我假设)会用一些可重复的值填充数组,而当没有附加调试器时(即从命令行运行或使用Ctrl-F5)这些值更加随机,有时会导致程序行为的微小偏差。不幸的是,调整是如此微妙以至于几乎无法察觉,而有问题的记忆在处理的第一个“帧”之后正确重置,但初始条件已经略有不同并且损坏已经造成。混沌理论在行动!感谢您的指导。

一个很好的调试技巧有帮助:编写一个自定义 malloc,立即用完全随机的数据填充内存。这样,您可以确保在使用它之前自己正确初始化它,否则每次运行它时您的结果都会(希望)疯狂——即使在调试模式下使用调试堆!

4

2 回答 2

17

如果在调试器下启动进程,Windows 堆的行为会有所不同。要禁用此行为(以便在调试时发现问题)添加 _NO_DEBUG_HEAP=1 到环境(如在这个问题中)。

或者,您可以在程序执行的早期附加到进程。Heap 将不会进入调试模式。在执行开始的某处添加DebugBreak()行,使用 Ctrl+F5 运行,并在被要求时开始调试。

于 2013-05-22T16:17:37.557 回答
0

好吧,如果不了解您的代码,就很难说。但是,我在执行大量浮点运算(双精度数)的程序时遇到了类似的问题。

当我处理略有不同但机器无法区分的数字时,问题就会出现。如果两个双精度数相差小于 numeric_limits<double>::epsilon(),则它们被视为机器的相同数字。因此,类型的表达式:

if (num1==num2)...

或者

if (num1<num2)...
...

可以产生丰富多彩的效果。

在调试或发布模式下运行时,这些彩色效果会有所不同。原因是调试/发布运行时库不同。此外,至关重要的是,编译是通过不同的代码优化完成的。命令行调试版本和调试窗口版本 (F5) 之间的差异也可以通过细微的优化差异来解释。

如果您使用的是 VS,您可以在菜单的C/C++andLinker部分查看不同编译选项和优化的效果。Properties

为避免此问题,我建议使用STL 库中的numeric_limits工具。<limits>例如,小于运算符的实现应该是这样的:

bool operator<(double num1, double num2) {
    double difference=fabs(num1-num2);
    if (difference>numeric_limits<double>::epsilon()) {
        if (num1 < num2) return true;
        return false;
    }
    return false;
}
于 2013-05-23T10:19:07.533 回答