C++ 发布版本与调试版本的运行方式不同的一些原因是什么

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/312312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 14:33:35  来源:igfitidea点击:

What are some reasons a Release build would run differently than a Debug build

c++visual-studiovisual-studio-2005

提问by BeachRunnerFred

I have a Visual Studio 2005 C++ program that runs differently in Release mode than it does in Debug mode. In release mode, there's an (apparent) intermittent crash occurring. In debug mode, it doesn't crash. What are some reasons that a Release build would work differently than a Debug build?

我有一个 Visual Studio 2005 C++ 程序,它在发布模式下的运行方式与在调试模式下的运行方式不同。在发布模式下,会发生(明显的)间歇性崩溃。在调试模式下,它不会崩溃。发布版本与调试版本的工作方式不同的一些原因是什么?

It's also worth mentioning my program is fairly complex and uses several 3rd party libraries for XML processing, message brokering, etc...

还值得一提的是,我的程序相当复杂,并使用了几个 3rd 方库进行 XML 处理、消息代理等......

Thanks in advance!

提前致谢!

回答by peterchen

Surviving the Release Versiongives a good overview.

在发布版本中幸存下来提供了一个很好的概述。

Things I have encountered - most are already mentioned

我遇到的事情 - 大多数已经提到

Variable initializationby far the most common. In Visual Studio, debug builds explicitely initialize allocated memory to given values, see e.g. Memory Valueshere. These values are usually easy to spot, cause an out of bounds error when used as an index or an access violation when used as a pointer. An uninitialized boolean is true, however, and may cause uninitialized memory bugs going undetected for years.

变量初始化是迄今为止最常见的。在 Visual Studio 中,调试构建显式地将分配的内存初始化为给定的值,参见例如此处的内存值。这些值通常很容易被发现,当用作索引时会导致越界错误,或者当用作指针时会导致访问冲突。然而,未初始化的布尔值是真的,并且可能导致未初始化的内存错误多年未被发现。

In Release builds where memory isn't explicitely initialized it just keeps the contents that it had before. This leads to "funny values" and "random" crashes, but as often to deterministic crashes that require an apparently unrelated command to be executed before the command that actually crashes. This is caused by the first command "setting up" the memory location with specific values, and when the memory locations are recycled the second command sees them as initializations. That's more common with uninitialized stack variables than heap, but the latter has happened to me, too.

在内存未明确初始化的 Release 版本中,它只保留以前的内容。这会导致“有趣的值”和“随机”崩溃,但通常会导致确定性崩溃,需要在实际崩溃的命令之前执行明显不相关的命令。这是由第一个命令“设置”具有特定值的内存位置引起的,当内存位置被回收时,第二个命令将它们视为初始化。未初始化的堆栈变量比堆更常见,但后者也发生在我身上。

Raw memory initialization can also be different in a release build whether you start from visual studio (debugger attached) vs. starting from explorer. That makes the "nicest" kind of release build bugs that never appear under the debugger.

无论您是从 Visual Studio(附加调试器)还是从资源管理器开始,在发布版本中的原始内存初始化也可能不同。这使得“最好”的发布构建错误永远不会出现在调试器下。

Valid Optimizationscome second in my exeprience. The C++ standard allows lots of optimizations to take place which may be surprising but are entirely valid e.g. when two pointers alias the same memory location, order of initialization is not considered, or multiple threads modify the same memory locations, and you expect a certain order in which thread B sees the changes made by thread A. Often, the compiler is blamed for these. Not so fast, young yedi! - see below

在我的经验中,有效优化排在第二位。C++ 标准允许进行许多可能令人惊讶但完全有效的优化,例如当两个指针别名相同的内存位置、不考虑初始化顺序或多个线程修改相同的内存位置,并且您期望某个顺序时其中线程 B 看到线程 A 所做的更改。通常,编译器会因这些而受到指责。没那么快,年轻的野地!- 见下文

TimingRelease builds don't just "run faster", for a variety of reasons (optimizations, logging functions providing a thread sync point, debug code like asserts not executed etc.) also the relative timing between operations change dramatically. Most common problem uncovered by that is race conditions, but also deadlocks and simple "different order" execution of message/timer/event-based code. Even though they are timing problems, they canbe surprisingly stable across builds and platforms, with reproductions that "work always, except on PC 23".

定时发布构建不仅“运行得更快”,出于各种原因(优化、提供线程同步点的日志功能、未执行断言等调试代码等),操作之间的相对时间也会发生巨大变化。最常见的问题是竞争条件,但也有死锁和基于消息/定时器/事件的代码的简单“不同顺序”执行。即使他们是时机的问题,他们可以是出奇地稳定整个“永远的工作,除了在PC 23”建立和平台,用复制品这一点。

Guard Bytes. Debug builds often put (more) guard bytes around selected instances and allocations, to protect against index overflows and sometimes underflows. In the rare cases where the code relies on offsets or sizes, e.g. serializing raw structures, they are different.

保护字节。调试构建通常在选定的实例和分配周围放置(更多)保护字节,以防止索引溢出和有时下溢。在代码依赖偏移量或大小的极少数情况下,例如序列化原始结构,它们是不同的。

Other code differencesSome instructions - e.g asserts - evaluate to nothing in release builds. Sometimes they have different side effects. This is prevalent with macro trickery, as in the classic (warning: multiple errors)

其他代码差异一些指令 - 例如断言 - 在发布版本中评估为空。有时它们有不同的副作用。这在宏技巧中很普遍,就像在经典中一样(警告:多个错误)

#ifdef DEBUG
#define Log(x) cout << #x << x << "\n";
#else 
#define Log(x)
#endif

if (foo)
  Log(x)
if (bar)
  Run();

Which, in a release build, evaluates to if (foo && bar)This type of error is very very rare with normal C/C++ code, and macros that are correctly written.

其中,在发布版本中,评估为if (foo && bar)这种类型的错误对于正常的 C/C++ 代码和正确编写的宏来说非常罕见。

Compiler BugsThis really never ever happens. Well - it does, but you are for the most part of your career better off assuming it does not. In a decade of working with VC6, I found one where I am still convinced this is an unfixed compiler bug, compared to dozens of patterns (maybe even hundreds of instances) with insufficient understanding of the scripture (a.k.a. the standard).

编译器错误这真的从来没有发生过。嗯 - 确实如此,但在你职业生涯的大部分时间里,假设它不存在会更好。在使用 VC6 的十年中,我发现一个我仍然相信这是一个未修复的编译器错误的地方,与对圣经(也就是标准)理解不足的数十种模式(甚至可能有数百个实例)相比。

回答by flolo

In debug version often assertions and/or debug symbols are enabled. This can lead to different memory layout. In case of a bad pointer, overflow of an array or similar memory access you access in one case critical bad memory (e.g. function pointer) and in other case maybe just some non-critical memory (e.g. just a doc string is trashed)

在调试版本中,通常会启用断言和/或调试符号。这会导致不同的内存布局。如果出现错误指针、数组溢出或类似内存访问,您在一种情况下访问严重坏内存(例如函数指针),而在另一种情况下可能只是一些非关键内存(例如,只是一个文档字符串被丢弃)

回答by Burkhard

Variables that are not initialized explicitly will or might not be zeroed in Release build.

未显式初始化的变量将或可能不会在发布版本中归零。

回答by Eugene Yokota

Release build (hopefully) would run faster than your debug build. If you are using more than one thread, you might see more interleaving, or simply one thread running faster than the others, which you may have not noticed in debug build.

发布版本(希望)会比您的调试版本运行得更快。如果您使用多个线程,您可能会看到更多的交错,或者只是一个线程比其他线程运行得更快,这在调试版本中您可能没有注意到。

回答by fluffels

Release builds are usually compiled with optimisation enabled in the compiler, while debug builds usually are not.

发布版本通常在编译器中启用优化的情况下编译,而调试版本通常不是。

In some languages or when using many different libraries this can cause intermittent crashes - especially when the chosen optimization level is very high.

In some languages or when using many different libraries this can cause intermittent crashes - especially when the chosen optimization level is very high.

I know that this is the case with the gcc C++ compiler, but I'm not sure about Microsoft's compiler.

我知道 gcc C++ 编译器就是这种情况,但我不确定 Microsoft 的编译器。

回答by Nik Reiman

I had a similar problem not long ago, which ended up being caused by the stack being treated differently in release builds. Other things that can differ:

不久前我遇到了类似的问题,最终是由于在发布版本中对堆栈的处理方式不同造成的。其他可能不同的事情:

  • Memory allocation is handled differently with debug builds in the VS compiler (ie, writing 0xcc over cleared memory, etc.)
  • Loop unrolling and other compiler optimizations
  • Alightment of pointers
  • VS 编译器中的调试版本对内存分配的处理方式不同(即,将 0xcc 写入已清除的内存等)
  • 循环展开和其他编译器优化
  • 指针的点亮

回答by David Rodríguez - dribeas

It depends both in the compiler vendor and the libraries you compile with the DEBUG flags. While DEBUG code should never affect running code (should have no side effects) it sometimes does.

这取决于编译器供应商和您使用 DEBUG 标志编译的库。虽然 DEBUG 代码永远不应该影响正在运行的代码(应该没有副作用),但它有时会影响。

In particular, variables may be initialized only in DEBUG mode and left uninitialized in RELEASE mode. The STL in Visual Studio compilers are different in DEBUG and RELEASE modes. The idea is that iterators are fully checked in DEBUG to detect possible errors (using invalidated iterators, for example an iterator into a vector is invalidated if an insertion occurs after the iterator is retrieved).

特别是,变量只能在 DEBUG 模式下初始化,而在 RELEASE 模式下未初始化。Visual Studio 编译器中的 STL 在 DEBUG 和 RELEASE 模式下是不同的。这个想法是在 DEBUG 中完全检查迭代器以检测可能的错误(使用无效的迭代器,例如,如果在检索到迭代器后发生插入,则到向量的迭代器将失效)。

The same happens with third party libraries, the first that I can think of is QT4, that will terminate your program with an assert if a thread different to the one that created the graphical object performs painting operations.

第三方库也会发生同样的情况,我能想到的第一个是 QT4,如果与创建图形对象的线程不同的线程执行绘制操作,它将使用断言终止您的程序。

With all the changes, your code and memory footprint will be different in both modes. A pointer (reading the position one pass the end of an array) problem may pass undetected if that position is readable.

随着所有更改,您的代码和内存占用在两种模式下都会有所不同。如果该位置可读,则指针(读取通过数组末尾的位置)问题可能未被检测到。

Assertions are meant to kill the application during DEBUG and disappear from RELEASE builds, so I would not think on assertions as being your problem. A rogue pointer or accessing one past the end would be my first suspects.

断言旨在在调试期间终止应用程序并从 RELEASE 构建中消失,因此我不会认为断言是您的问题。流氓指针或访问结束后的指针将是我的第一个嫌疑人。

Some time ago there were problems with some compiler optimizations breaking code, but I have not read complaints lately. There could be an optimization problem there, but this would not be my first suspect.

前一段时间,一些编译器优化破坏了代码,但我最近没有阅读投诉。那里可能存在优化问题,但这不是我的第一个怀疑。

回答by David Rodríguez - dribeas

http://www.debuginfo.com/tips/userbpntdll.html

http://www.debuginfo.com/tips/userbpntdll.html

Due to the fact that guard bytes are added in debug builds, you may be able to "safely" access memory that is out-of-bounds for an array (particularly dynamic arrays), but this will cause an access violation in the release build. This error might go unnoticed, causing a corrupt heap and possibly an access violation in a place unrelated to the original error.

由于在调试版本中添加了保护字节这一事实,您可能能够“安全地”访问超出数组(特别是动态数组)范围的内存,但这将导致发布版本中的访问冲突. 此错误可能会被忽视,导致堆损坏,并可能在与原始错误无关的地方发生访问冲突。

Use PageHeap (or, if you have Debugging Tools installed you could use gflags) to discover bugs related to corrupt heaps.

使用 PageHeap(或者,如果您安装了调试工具,则可以使用 gflags)来发现与损坏的堆相关的错误。

http://support.microsoft.com/?id=286470

http://support.microsoft.com/?id=286470

回答by FL4SOF

This post along with the links provided is very helpful in fixing related bug. adding to the above list, difference in calling conventions can also lead to this behavior - It failed in release build only with optimization for me. i declared as __stdcall and defined as __cdecl ( by default). [strangely this warning is not picked even in warn level 4 MSVC?]

这篇文章以及提供的链接对于修复相关错误非常有帮助。添加到上面的列表中,调用约定的差异也可能导致这种行为 - 它在发布版本中失败,仅对我进行了优化。我声明为 __stdcall 并定义为 __cdecl (默认情况下)。[奇怪的是,即使在警告级别 4 MSVC 中也没有选择此警告?]

回答by brahmin

Among optimizations that can be performed in Release mode (and no in Debug), copy elision can yield different results. More specifically RVO (return value optimization), depending on how your constructors are designed.

在可以在 Release 模式下执行的优化(在 Debug 中不能执行)中,复制省略可以产生不同的结果。更具体地说,RVO(返回值优化),取决于您的构造函数的设计方式。

What are copy elision and return value optimization?

什么是复制省略和返回值优化?