debug与release区别(转载+整理)

Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
Debug 和 Release 的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd/Fo,但区别并不重要,通常他们也不会引起 Release 版错误,在此不讨论)
Debug 版本
参数含义
/MDd /MLd 或 /MTd 使用 Debug runtime library (调试版本的运行时刻函数库)
/Od 关闭优化开关
/D "_DEBUG" 相当于 #define _DEBUG,打开编译调试代码开关 (主要针对assert函数)
/ZI 创建 Edit andcontinue(编辑继续)数据库,这样在调试过程中如果修改了源代码不需重新编译
/GZ 可以帮助捕获内存错误
/Gm 打开最小化重链接开关,减少链接时间

Release 版本

参数含义
/MD /ML 或 /MT 使用发布版本的运行时刻函数库
/O1 或 /O2 优化开关,使程序最小或最快
/D "NDEBUG" 关闭条件编译调试代码开关 (即不编译assert函数)
/GF 合并重复的字符串,并将字符串常量放到只读内存,防止被修改

实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。
哪些情况下 Release 版会出错
有了上面的介绍,我们再来逐个对照这些选项看看 Release 版错误是怎样产生的
1、Runtime Library:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。
2、优化:这是造成错误的主要原因,因为关闭优化时源程序基本上是直接翻译的,而打开优化后编译器会作出一系列假设。这类错误主要有以下几种:
1. 帧指针(Frame Pointer)省略(简称FPO):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误,但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误是程序崩溃。
C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入/Oy-编译选项来关掉帧指针省略,以确定是否此类错误。此类错误通常有:MFC 消息响应函数书写错误。正确的应为:

afx_msg LRESULT OnMessageOwn
(WPARAM wparam, LPARAM lparam);

ON_MESSAGE 宏包含强制类型转换。防止这种错误的方法之一是重定义 ON_MESSAGE 宏,把下列代码加到 stdafx.h 中(在#include"afxwin.h"之后),函数原形错误时编译会报错。

#undef ON_MESSAGE
#define ON_MESSAGE(message, memberFxn) /
{
message, 0, 0, 0, AfxSig_lwl, /
(AFX_PMSG)(AFX_PMSGW)
(static_cast< LRESULT (AFX_MSG_CALL /
CWnd::*)(WPARAM, LPARAM) > (&memberFxn)
},

2. volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。
如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。
3. 变量优化:优化程序会根据变量的使用情况优化变量。例如,函数中有一个未被使用的变量,在 Debug 版中它有可能掩盖一个数组越界,而在 Release 版中,这个变量很可能被优化调,此时数组越界会破坏栈中有用的数据。当然,实际的情况会比这复杂得多。与此有关的错误有非法访问,包括数组越界、指针错误等。例如:

void fn(void)
{
  int i;
  i = 1;
  int a[4];
  {
    int j;
    j = 1;
  }
  a[-1] = 1;
  //当然错误不会这么明显,例如下标是变量
  a[4] = 1;
}

j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而Release 版由于 i、j 并未其很大作用可能会被优化掉,从而使栈被破坏。
3. DEBUG 与 NDEBUG :当定义了 _DEBUG 时,assert() 函数会被编译,而 NDEBUG 时不被编译。此外,TRACE() 宏的编译也受 _DEBUG 控制。
所有这些断言都只在 Debug版中才被编译,而在 Release 版中被忽略。唯一的例外是VERIFY()。事实上,这些宏都是调用了assert()函数,只不过附加了一些与库有关的调试代码。如果你在这些宏中加入了任何程序代码,而不只是布尔表达式(例如赋值、能改变变量值的函数调用等),那么Release版都不会执行这些操作,从而造成错误。初学者很容易犯这类错误,查找的方法也很简单,因为这些宏都已在上面列出,只要利用VC++ 的 Find in Files 功能在工程所有文件中找到用这些宏的地方再一一检查即可。另外,有些高手可能还会加入 #ifdef _DEBUG 之类的条件编译,也要注意一下。
顺便值得一提的是VERIFY()宏,这个宏允许你将程序代码放在布尔表达式里。这个宏通常用来检查 Windows API的返回值。有些人可能为这个原因而滥用VERIFY(),事实上这是危险的,因为VERIFY()违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。
4. /GZ 选项:这个选项会做以下这些事:
1. 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD (Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( DeadData ) 填充已被释放的堆内存(例如 delete ),0xFD(deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问),其中括号中的词是微软建议的助记词。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用0来初始化变量,这是错误的(而且这样很不利于查找错误)。
2. 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性。(防止原形不匹配)
3. 函数返回前检查栈指针,确认未被修改。(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。除此之外,/Gm/GF等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。
怎样“调试” Release 版的程序
遇到Debug成功但Release失败,显然是一件很沮丧的事,而且往往无从下手。如果你看了以上的分析,结合错误的具体表现,很快找出了错误,固然很好。但如果一时找不出,以下给出了一些在这种情况下的策略。
1. 前面已经提过,Debug和Release只是一组编译选项的差别,实际上并没有什么定义能区分二者。我们可以修改Release版的编译选项来缩小错误范围。如上所述,可以把Release的选项逐个改为与之相对的Debug选项,如/MD改为/MDd、/O1改为/Od,或运行时间优化改为程序大小优化。注意,一次只改一个选项,看改哪个选项时错误消失,再对应该选项相关的错误,针对性地查找。这些选项在Project/Settings...中都可以直接通过列表选取,通常不要手动修改。由于以上的分析已相当全面,这个方法是最有效的。
2. 在编程过程中就要时常注意测试 Release 版本,以免最后代码太多,时间又很紧。
3. 在 Debug 版中使用 /W4 警告级别,这样可以从编译器获得最大限度的错误信息,比如 if( i =0 )就会引起 /W4 警告。不要忽略这些警告,通常这是你程序中的 Bug 引起的。但有时 /W4 会带来很多冗余信息,如未使用的函数参数警告,而很多消息处理函数都会忽略某些参数。我们可以用:

#progma warning(disable: 4702)
//禁止
//...
#progma warning(default: 4702)
//重新允许来暂时禁止某个警告,或使用
#progma warning(push, 3)
//设置警告级别为 /W3
//...
#progma warning(pop)
//重设为 /W4
来暂时改变警告级别,有时你可以只在认为可疑的那一部分代码使用 /W4。
4. 你也可以像Debug一样调试你的Release版,只要加入调试符号。在Project/Settings...中,选中 Settings for "Win32 Release",选中 C/C++ 标签,Category 选 General,Debug Info 选 Program Database。再在 Link 标签 Projectoptions 最后加上 "/OPT:REF" (引号不要输)。这样调试器就能使用 pdb 文件中的调试符号。
但调试时你会发现断点很难设置,变量也很难找到??这些都被优化过了。不过令人庆幸的是,CallStack窗口仍然工作正常,即使帧指针被优化,栈信息(特别是返回地址)仍然能找到。这对定位错误很有帮助

 

 

 

 

 

1. 量。
大家都知道,debugrelease在初始化所做的操作是不同的,debug是将每个字位都0xcc(1),而release赋值似于随机(我想是直接从内存中分配的,没有初始化)这样就明确了,如果你的程序中的某个量没被初始化就被引用,就很有可能出异常:用作控制量将致流程向不一致;用作数将会使程序崩;更加可能是造成其他量的不准确而引起其他的错误。所以在声明量后其初始化一个默是最简单有效的法,否则项目大了你找都没地方找。代存在错误debug方式下可能会忽略而不被察到,如debug方式下数越界也大多不会出,在 release中就暴露出来了,个找起来就比较难:( 是自己多加注意吧
呵呵,就是我犯的问题~~
2.
自定消息的消息参数。
MFC
提供了很好的消息机制,更增加了自定消息,好我就不用多了。也存在debugrelease问题吗?答案是肯定的。在自定消息的函数体声明常会看到这样的写法:afx_msg LRESULTOnMessageOwn(); Debug情况下一般不会有任何问题,而当你在Release下且多线程或使用了消息传递时就会致无效句柄之错误错误直接原因是消息体的参数没有添加,即应该写成:afx_msg LRESULT OnMessageOwn(WPARAM wparam,LPARAM lparam); (2)
3. release
模式下不出,但debug模式下报错
种情况下大多也是因码书写不正确引起的,MFC的源,可以发现好多ASSERT(断言)个宏只是在debug模式下才有效,那么就清楚了,release版不报错是忽略了错误而不是没有错误可能存在很大的患,因Debug模式下,比方便调试,好好的检查自己的代,再此就不多了。
4. ASSERT, VERIFY, TRACE..........调试
种情况很容易解个例子:VCASSERT然后中按F12跳到宏定的地方,里你就能够发现DebugASSERT AfxAssertFailedLine,而Release下的宏定"#define ASSERT(f) ((void)0)"所以注意在调试宏的句不要用程序相关量如i++写操作的句。VERIFY是个例外,"#defineVERIFY(f) ((void)(f))",即行,里的作用就不多追究了,有趣可自己研究:)

总结
Debug
Release不同的问题开始写代码时生,99%是因你的代码书错误致的,所以不要统问题编译问题努力找找自己的原因才是根本。我从前就常常遇到情况,经历过一次次的教后我就开始注意了,在我所写的代我已好久没遇到问题了。下面是几个避免的方面,即使没有问题注意一下:
1.
注意量的初始化,尤其是指针变量,数组变量的初始化(很大的情况下另作考)
2.
自定消息及其他声明的准写法
3.
使用调试使用后最好注
4.
尽量使用try - catch(...)
5.
尽量使用模,不但表达清楚而且方便调试
1
debug
版初始化成0xcc是因0xccx86下是一条int 3步中断指令,这样程序如果跑了遇到0xcc就会停下来,片机一般将没用的代填入jmp 0000句是一

1.   量未初始化。下面的程序在debug中运行的很好。  
   
  
thing    *   search(thing    *    something)  
   BOOL    found;  
   for(int    i    =   0;    i    <   whatever.GetSize();    i++)  
   {  
   if(whatever[i]->field    ==   something->field)  
   {    /*    found   it    */  
   found    =    TRUE;  
   break;  
   }    /*    found   it    */  
   }  
   if(found)  
   return    whatever[i];  
   else  
   return    NULL;  
  
而在release中却不行,因debug中会自动给变量初始化found=FALSE,而在release版中不会。所以尽可能的给变量、构初始化。  
   
   2.   
数据溢出的问题  
   
  
如:char    buffer[10];  
   int    counter;  
   
   lstrcpy(buffer,   "abcdefghik");  
   
  
debug版中bufferNULL覆盖了counter的高位,但是除非counter>16M,什么问题也没有。但是在release版中,counter可能被放在寄存器中,这样NULL就覆盖了buffer下面的空,可能就是函数的返回地址,ACCESS    ERROR  
   
   3.    DEBUG
版和RELEASE版的内存分配方式是不同的    。如果你在DEBUG版中申    ele        6*sizeof(DWORD)=24bytes,实际上分配你的是32bytesdebug版以32bytes为单位分配),    而在release版,分配你的就是24bytesrelease版以8bytes为单位),所以在debug版中如果你写ele[6],可能不会有什么问题,而在release版中,就有ACCESS    VIOLATE  
   
   II.    ASSERT
VERIFY  
   
   1.    ASSERT
Release版本中是不会被编译的。  
   
   ASSERT
宏是这样  
   
   #ifdef    _DEBUG  
   #define    ASSERT(x)   if(    (x)    ==   0)    report_assert_failure()  
   #else  
   #define    ASSERT(x)  
   #endif  
  
实际上复一些,但无关要。假如你在句中加了程序中必要有的代  
  
比如  
   
   ASSERT(pNewObj    =   new    CMyClass);  
   
   pNewObj->MyFunction();  
   
  
Release版本中的pNewObj不会分配到空  
   
  
所以行到下一个句的候程序会报该程序行了非法操作的错误这时可以用VERIFY      
   
   #ifdef    _DEBUG  
   #define    VERIFY(x)   if(    (x)    ==    0)   report_assert_failure()  
   #else  
   #define    VERIFY(x)   (x)  
   #endif  
  
这样,代release版中就可以行了。  
   
   III.   
参数问题  
   
  
自定消息的理函数,必如下:  
   
   afx_msg    LRESULT   OnMyMessage(WPARAM,    LPARAM);  
   
  
返回HRESULT型,否Debug,而Release  
   
   IV.   
内存分配  
   
  
数据建和清除的一性:如果一个DLL提供一个能够创建数据的函数,那么DLL时应该提供一个函数销毁这些数据。数据的建和清除应该在同一个次上。  
   
   V.    DLL
的灾  
   
  
将不同版本DLL混合造成的不一致性形象的称   “动态连的地“(DLL    Hell)    ,甚至微自己也http://msdn.microsoft.com/library/techart/dlldanger1.htm)  
   
  
如果你的程序使用你自己的DLL时请注意:  
   
   1.   
不能将debugrelease版的DLL混合在一起使用。debug都是debug版,release版都是release版。  
   
  
解决法是将debugrelease的程序分放在主程序的debugrelease  
   
   
   2.   
千万不要以态连会解决问题,那只会使情况更糟糕。  
   
  
VI.    RELEASE板中的调试      
   
   1.   
ASSERT()       VERIFY()    。找出定"#ifdef    _DEBUG"中的代,如果在RELEASE版本中需要些代码请将他移到定外。TRACE(...)中代,因为这些代RELEASE中也不被编译    请认检查那些在RELEASE中需要的代是否并没有被便宜。  
   
   2.   
量的初始化所来的不同,在不同的系,或是在DEBUG/RELEASE版本都存在这样的差异,所以请对变行初始化。  
   
   3.   
是否在编译时有了警告?将警告级别设34,然后保编译时没有警告出.  
   
  
VII.    Project    Settings"        "C++/C    "    目下选项DisbaleDebug)。编译器的化可能多意想不到的错误http://www.pgh.net/~newcomer/debug_release.htm  
   
   1.   
此外RELEASE版本的件也可以调试做如下改  
   
  
"Project    Settings"        "C++/C    "    目下   "category"        "General"    并且将"Debug    Info"   "Program    Database"  
   
  
"Link"目下"Generate    Debug    Info"检查框。  
   
   "Rebuild    All"  
   
  
如此做法会生的一些限制:  
   
  
无法得在MFC    DLL中的量的  
   
  
须对该软件所使用的所有DLL工程都行改  
   
  
另:  
   
   MS    BUG
MS的一份技文档中表明,在VC5DLL"Maximize    Speed"选项并未被完全支持,因此将会引起内存错误致程序崩  
   
   2.   
http://www.sysinternals.com/有一个程序DebugView,用来捕捉OutputDebugString出,运行起来后(估是自设为system    debugger)就可以看所有程序的OutputDebugString出。此后,你可以脱离VC来运行你的程序并调试信息。  
   
   3.   
有一个叫Gimpel    Lint的静码检查工具,据好用http://www.gimpel.com/    要化$的。

 


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可 6私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值