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

新手上路,请多包涵

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

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

提前致谢!

原文由 BeachRunnerFred 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 372
2 个回答

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

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

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

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

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

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

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

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

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

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

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

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

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

原文由 peterchen 发布,翻译遵循 CC BY-SA 4.0 许可协议

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

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

原文由 brahmin 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题