72

仅在发布编译模式下出现但在调试模式下不会出现的错误和异常程序行为的典型原因是什么?

4

18 回答 18

35

很多时候,在 C++ 的调试模式下,所有变量都被初始化为空,而在发布模式下不会发生同样的情况,除非明确说明。

检查任何调试宏和未初始化的变量

您的程序是否使用线程,那么优化也会导致发布模式下的一些问题。

还要检查所有异常,例如与发布模式没有直接关系,但有时我们只是忽略一些关键异常,例如 VC++ 中的内存访问冲突,但至少在 Linux、Solaris 等其他操作系统中同样可能是一个问题。理想情况下,您的程序不应捕获此类关键异常,例如访问 NULL 指针。

于 2009-11-19T09:49:56.637 回答
21

一个常见的缺陷是在 ASSERT 中使用具有副作用的表达式。

于 2009-11-19T09:52:45.033 回答
12

I've been bitten by a number of bugs in the past that have been fine in Debug builds but crash in Release builds. There are many underlying causes (including of course those that have already been summarised in this thread) and I've been caught out by all of the following:

  • Member variables or member functions in an #ifdef _DEBUG, so that a class is a different size in a debug build. Sometimes #ifndef NDEBUG is used in a release build
  • Similarly, there's a different #ifdef which happens to be only present in one of the two builds
  • The debug version uses debug versions of the system libraries, especially the heap and memory allocation functions
  • Inlined functions in a release build
  • Order of inclusion of header files. This shouldn't cause problems, but if you have something like a #pragma pack that hasn't been reset then this can lead to nasty problems. Similar problems can also occur using precompiled headers and forced includes
  • Caches: you may have code such as caches that only gets used in release builds, or cache size limits that are different
  • Project configurations: the debug and release configurations may have different build settings (this is likely to happen when using an IDE)
  • Race conditions, timing issues and miscellanous side-effects occurring as a result of debug only code

Some tips that I've accumulated over the years for getting to the bottom of debug/release bugs:

  • Try to reproduce anomalous behaviour in a debug build if you can, and even better, write a unit test to capture it
  • Think about what differs between the two: compiler settings, caches, debug-only code. Try to minimise those differences temporarily
  • Create a release build with optimisations switched off (so you're more likely to get useful data in the debugger), or an optimised debug build. By minimising the changes between debug and release, you're more likely to be able to isolate which difference is causing the bug.
于 2010-05-11T13:19:53.663 回答
9

其他差异可能是:

  • 在垃圾收集语言中,收集器通常在释放模式下更具侵略性;
  • 内存的布局可能经常不同;
  • 内存可能会以不同的方式初始化(​​例如,可以在调试模式下归零,或者在发布时更积极地重新使用);
  • 局部变量可能会被提升为在发布时注册值,这可能会导致浮点值出现问题。
于 2009-11-19T09:52:42.803 回答
3

是的!,如果你有条件编译,可能会有时序错误(优化的发布代码节,非优化的调试代码),内存重用与调试堆。

于 2009-11-19T09:51:22.957 回答
3

它可以,特别是如果你在 C 领域。

一个原因可能是 DEBUG 版本可能会添加代码来检查杂散指针并以某种方式保护您的代码免于崩溃(或行为不正确)。如果是这种情况,您应该仔细检查从编译器获得的警告和其他消息。

另一个原因可能是优化(通常在发布版本时打开,在调试时关闭)。代码和数据布局可能已经过优化,例如,当您的调试程序只是访问未使用的内存时,发布版本现在正在尝试访问保留的内存甚至指向代码!

编辑:我看到其他人提到它:当然,如果不在调试模式下编译,您可能有条件排除的整个代码部分。如果是这样的话,我希望这真的是在调试代码,而不是对程序本身的正确性至关重要!

于 2009-11-19T09:54:00.993 回答
3

CRT 库函数在调试与发布(/MD 与 /MDd)中的行为不同。

例如,调试版本通常会预先填充您传递到指定长度的缓冲区以验证您的声明。示例包括strcpy_s,StringCchCopy等。即使字符串提前终止,您的szDest最好是n字节长!

于 2009-11-19T12:25:07.787 回答
2

当然,例如,如果您使用类似的结构

#if DEBUG

//some code

#endif
于 2009-11-19T09:49:48.890 回答
1

您需要提供更多信息,但是是的,这是可能的。这取决于您的调试版本的功能。您很可能有日志记录或额外检查,这些检查不会编译到发布版本中。这些仅用于调试的代码路径可能会产生意想不到的副作用,这些副作用会以奇怪的方式改变状态或影响变量。调试构建通常运行较慢,因此这可能会影响线程并隐藏竞争条件。对于发布编译的直接优化也是如此,发布编译可能会(尽管现在不太可能)将某些东西作为优化短路。

于 2009-11-19T09:49:42.440 回答
1

如果没有更多细节,我将假设“不正常”意味着它要么无法编译,要么在运行时抛出某种错误。检查您是否有依赖于编译版本的代码,无论是通过#if DEBUG语句还是通过标有Conditional属性的方法。

于 2009-11-19T09:50:59.310 回答
1

在 .NET 中,即使您不使用 .NET 之类#if DEBUG的条件编译,编译器在发布模式下的优化仍然比在调试模式下更加自由,这也可能导致仅发布错误。

于 2009-11-19T09:51:54.487 回答
1

有些编译器优化可能会破坏有效代码,因为它们过于激进。

尝试在打开较少优化的情况下编译您的代码。

于 2009-11-19T10:03:29.050 回答
1

在非 void 函数中,所有执行路径都应以 return 语句结束。

在调试模式下,如果您忘记使用 return 语句结束这样的路径,则该函数通常默认返回 0。

但是,在发布模式下,您的函数可能会返回垃圾值,这可能会影响程序的运行方式。

于 2013-04-07T06:17:01.467 回答
1

这是可能的,如果你有条件编译使得调试代码和发布代码不同,并且代码中存在一个只在发布模式下使用的错误。

除此之外,这是不可能的。调试代码和发布代码的编译方式存在差异,代码在调试器下运行与否的执行方式也存在差异,但如果这些差异中的任何一个导致性能差异之外的任何其他原因,那么问题一直存在。

在调试版本中,错误可能不会发生(因为时间或内存分配不同),但这并不意味着错误不存在。可能还有其他与调试模式无关的因素改变了代码的时序,导致错误发生与否,但归根结底,如果代码正确,错误就不会发生在任何情况下。

所以,不,调试版本不行,因为你可以运行它而不会出错。如果在发布模式下运行时出现错误,不是因为发布模式,而是因为从一开始就存在错误。

于 2009-11-19T10:11:41.693 回答
0

这是可能的。如果发生这种情况并且不涉及条件编译,那么您可以很确定您的程序是错误的,并且只是因为偶然的内存初始化甚至内存中的布局而在调试模式下工作!

于 2009-11-19T10:00:09.127 回答
0

我刚刚经历过,当我调用一个没有恢复寄存器以前值的汇编函数时。

在“发布”配置中,VS 使用 /O2 进行编译,这优化了代码的速度。因此,一些局部变量仅映射到与上述函数共享的 CPU 寄存器(用于优化),从而导致严重的内存损坏。

无论如何,看看您是否没有在代码中的任何地方间接弄乱 CPU 寄存器。

于 2015-05-22T13:43:15.267 回答
0

我记得之前我们在 c/c++ 中构建 dll 和 pdb。

我记得这个:

  • 添加日志数据有时会使错误移动或消失或出现完全其他错误(因此它不是一个真正的选项)。
  • 其中许多错误与 strcpy 和 strcat 中的 char 分配以及 char[] 等数组有关...
  • 我们通过运行边界检查器清除了一些内存分配/释放问题。
  • 很多时候,我们系统地检查了代码并修复了一个字符分配。
  • 我的两分钱是它与内存分配和管理以及调试模式和释放模式之间的约束和区别有关。

然后继续经历那个循环。

我们有时会临时将发布版本换成 dll 的调试版本,以便在处理这些错误时不推迟生产。

于 2015-10-15T13:01:37.397 回答
0

另一个原因可能是数据库调用。您是否在同一线程中多次保存和更新同一记录,有时是为了更新。更新可能失败或未按预期工作,因为先前的创建命令仍在处理中,并且对于更新,db 调用未能找到任何记录。这不会在调试中发生,因为调试器会确保在着陆前完成所有待处理的任务。

于 2018-12-31T06:04:21.943 回答