Debug&Release
rT7hS(^0
,F1L7i$nA%}a0Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。51Testing软件测试网&jXR`+e3w:N
R.S9Gd'o8|X8pf+B0Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd /Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)
8}
gc!N4jk#_nU)I+@051Testing软件测试网#W/j*H Vj
Debug 版本
8L,iPQ? j051Testing软件测试网PF3I5bM-uM\.g
参数 | 含义 |
/MDd /MLd 或 /MTd | 使用 Debug runtime library (调试版本的运行时刻函数库) |
/Od | 关闭优化开关 |
/D | "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关 (主要针对assert函数) |
/ZI | 创建 Edit and continue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译 |
/GZ | 可以帮助捕获内存错误 |
/Gm | 打开最小化重链接开关, 减少链接时间 |
3}&?X.r@l2h
gI4M-B051Testing软件测试网+]R"XN1fN }N
51Testing软件测试网WKy}:O}
Release 版本51Testing软件测试网&Vn6x'[Q)C%H;OB
51Testing软件测试网H S1Zkdz
51Testing软件测试网L!G? IMzrt8T
参数 | 含义 |
/MD /ML 或 /MT | 使用发布版本的运行时刻函数库 |
/O1 或 /O2 | 优化开关,使程序最小或最快 |
/D | "NDEBUG" 关闭条件编译调试代码开关 (即不编译assert函数) |
/GF | 合并重复的字符串, 并将字符串常量放到只读内存, 防止被修改 |
51Testing软件测试网7H&Yv2}D KB
'@~4C1w3?-K
h"\l}/`0实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。51Testing软件测试网U?l3@?$O#a?l
-Pb9A'I8yv2M/n0哪些情况下 Release 版会出错
"sc ]"tr? CO6U051Testing软件测试网O(I[wr:u9Q+X#u#t
有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的
J2x{,`|/Het%kKc051Testing软件测试网)o1jBA'p9w"fLT%s
1、Runtime Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。51Testing软件测试网p2E4W.p ^M"wcAW
^.i2}c2v02、优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:51Testing软件测试网_1HC@:LRh'R
51Testing软件测试网5zE9l#[b E[;?NU
1. 帧指针(Frame Pointer)省略(简称FPO):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误,但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。
$Kl&QJ.O0
-J-Sl+HyzQ!R Hj0C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入/Oy-编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:MFC 消息响应函数书写错误。正确的应为:
+Xd8c$j(b'm0]+p051Testing软件测试网
US,kOn g%bsFL&Kz
|
%sF*|1EZ;~&|0
3F+it5o5yF8_2a1L051Testing软件测试网ezNc7]9vm
ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include "afxwin.h"之后),函数原形错误时编译会报错。51Testing软件测试网,lUE9v9xtDSiB
xN8[K0xOxk,VJ)\0
|
O
{RB.DH/u0
A@3L+JW)J2{ p"v02. volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。
fBh#M(^%v7d0
6a
__$M5A/d#u!R0如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。
z2x{
J!\03. 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有非法访问,包括数组越界、指针错误等。例如:
2H2H`F h;l;Hr051Testing软件测试网E-f2uD-y;J3y
|
5h4Og*aT6}051Testing软件测试网@$[C$gYu ?
51Testing软件测试网&P {1W`QT1?K:D)u
j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而 Release 版由于 i、j 并未其很大作用可能会被优化掉,从而使栈被破坏。
HML9^!gP;w0