使用Visual Studio调试器与不使用调试器运行可执行文件之间的差异

Differences between running an executable with Visual Studio debugger vs without debugger

本文关键字:调试器 之间 可执行文件 运行 Studio Visual 使用      更新时间:2023-10-16

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

谁能列举一下这两种环境之间可能存在的差异?

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

以下是环境和结果:

  1. 直接从命令提示符(cmd)运行:不可重复输出
  2. 从Visual Studio运行调试(F5):可重复输出
  3. 在没有调试的情况下从Visual Studio运行(Ctrl-F5):不可重复输出

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

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

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

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

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

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

如果不了解您的代码,这很难说。然而,我在一个执行大量浮点运算(双精度数)的程序中遇到了类似的问题。

当我处理稍微不同的数字时,这个问题就会出现,但对于机器来说,数字是无法区分的。如果两个双精度数相差小于numeric_limits<double>::epsilon(),则它们在机器中被视为相同的数字。因此,类型为:

的表达式
if (num1==num2)...

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

可以产生彩色效果。

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

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

为了避免这个问题,我建议使用<limits> STL库中的numeric_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;
}