Release构建与Debug构建的运行方式不同的原因是什么

Bea*_*red 55 c++ visual-studio-2005 visual-studio

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

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

提前致谢!

pet*_*hen 127

幸存版本 提供了很好的概述.

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

变量初始化 是目前最常见的.在Visual Studio中,调试版本明确地将已分配的内存初始化为给定值,请参阅此处的内存值.这些值通常很容易被发现,当用作索引时会导致越界错误,或者当用作指针时会导致访问冲突.但是,未初始化的布尔值是正确的,并且可能导致未初始化的内存错误多年未被检测到.

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

无论是从visual studio(附加调试器)还是从explorer开始,原始内存初始化在发布版本中也可能不同.这使得"最好"的版本构建错误从未出现在调试器下面.

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

由于各种原因(优化,提供线程同步点的日志记录功能,未执行的断言等调试代码等),时序发布版本不仅"运行得更快",而且操作之间的相对时序也发生了显着变化.最常见的问题是竞争条件,但也有死锁和简单的"不同顺序"执行消息/定时器/基于事件的代码.即使它们是计时问题,它们在构建和平台上可以令人惊讶地保持稳定,复制"始终有效,除了在PC 23上".

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

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

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

if (foo)
  Log(x)
if (bar)
  Run();
Run Code Online (Sandbox Code Playgroud)

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

编译器错误这真的永远不会发生.嗯 - 确实如此,但是如果不这样做,那么你在职业生涯的大部分时间里会更好.在与VC6合作的十年中,我找到了一个我仍然相信这是一个未修复的编译器错误,与几十个模式(甚至数百个实例)相比,对经文(也就是标准)的理解不足.

  • 很棒的答案+1!建议接受 (2认同)
  • 太棒了,但是请添加一些有关断言副作用的内容。断言中的表达式可能有副作用,并且不会在发布版本中运行。例如病理情况:assert(n++ == 2); 有时,有一个替代宏总是对表达式求值,例如 VC++ 中的 VERIFY。 (2认同)
  • @KeyC0de:不是故意的。意思是说:可能有几十台机器运行不受影响,但一台机器(由于配置略有不同)会经常遇到问题。即一个难以重现和分析的错误,但“丑陋”足以给你带来麻烦。 (2认同)

flo*_*olo 6

在调试版本中,通常启用断言和/或调试符号.这可能导致不同的内存布局.如果指针错误,数组溢出或类似的内存访问,则在一种情况下访问一个严重的坏内存(例如函数指针),在其他情况下可能只是一些非关键内存(例如,只是一个doc字符串被删除)


Bur*_*ard 5

未明确初始化的变量将在发布版本中归零或不归零.