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

2021-12-07 00:00:00 visual-studio c++ visual-studio-2005

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

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?

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

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

提前致谢!

推荐答案

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

Surviving the Release Version gives a good overview.

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

Things I have encountered - most are already mentioned

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

Variable initialization by far the most common. In Visual Studio, debug builds explicitly initialize allocated memory to given values, see e.g. Memory Values here. 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.

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

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.

无论您是从 Visual Studio(附加调试器)还是从资源管理器开始,发布版本中的原始内存初始化也可能不同.这使得最好的"一种从未出现在调试器下的发布版本错误.

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.

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

Valid Optimizations come 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

时间 发布构建不仅运行得更快",出于多种原因(优化、提供线程同步点的日志功能、未执行断言等调试代码等)操作之间的相对时间发生了巨大变化.最常见的问题是竞争条件,但也有死锁和简单的不同顺序".执行基于消息/定时器/事件的代码.尽管它们是时间问题,但它们可以在不同版本和平台上出奇地稳定,复制始终有效,但在 PC 23 上除外".

Timing Release 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 can be surprisingly stable across builds and platforms, with reproductions that "work always, except on 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 differences Some 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 << "
";
#else 
#define Log(x)
#endif

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

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

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.

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

Compiler Bugs This 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).

相关文章