Debug 运行正常,Release版本不能够健康运行总结(转)

引言

    
如果当您的开销进程遭到遇到了宽广的不当,或许你的Release版本不能够正常运行而Debug版本运行无误,那么我引进而读书本文:因为不用假设你想象的那么,Release版本可以管你的应用程序可以象Debug版本一样运行。

若是你于开发阶段完成之后要以出展开一段时间之内向不曾进行过Release版本测试,然而当你测试的时光可发现问题,那么要看我们的调试规则1:

规则1:

  经常性对开发软件进行Debug和Release版本的正常化测试.
测试Release版本的日子距离越来越丰富,排除问题之难度进一步怪,至少对Release版本进行每周1不善的测试,可以要您于紧凑的开发周期内节省潜在的排故时间.
不要擅自去Release版本需要之代码这点看起像又显不过,但也是开发人员无意中时发作的左,原因在编译器编译Release版本时候会积极性消除在代码中有的极大,例如ASSERT和TRACE在Release版本会自动清除,这样造成的问题是公当这些宏当中运行的代码也被随后删除,这是甚惊险的政工J,例如:
ASSERT(m_ImageList.Create(MAKEINTRESOURCE(IDB_IMAGES), 16, 1,
RGB(255,255,255)));
这样的代码在Debug模式不见面拧,图像列表也自动创建了,然而在Release版本呢?后继使用m_ImageList对象仅见面造成程序的Crash!,因此ASSERT宏中尽量采用逻辑运算符作为验证。

规则 2:

  
不要拿代码放置在一味在某种编译选项中执的地方,对于下_DEBUG等编译选项宏内部的代码必须不影响总体程序的以。

规则 3:

  不要采用规则2作为评标准来删除ASSERT宏,ASSERT宏是个有效的工具,但易采取错误.
使Debug编译模式类似Release模式要你的Release版本存在的题目是由代码被编译器自动清除造成的,那么通过是措施你的题材可能会见再也现.
一些问题之发可能是由于不同编译选项间预定义符号造成的,因此你可以变更编译模式下的预定义符号,从而使你的Debug模式类似Release模式,观察错误是否发生,更改编译预定义符号方法如下:
a.. Alt-F7开拓项目设置,在C++/C
页面,选择”General”类别,更改”_DEBUG”符号为”NDEBUG”. b.. 在C++/C 页面,
选择”Preprocessor”类别,添加预定义符号”_DEBUG”到”Undefined Symbols”栏.
c.. 使用”Rebuild
All”重新编译如果经过者安装,您当Release编译模式下的题材在Debug模式下复出,那么请您依据以下步骤对你的代码进行改动:
a.. 查找ASSERT排除其中的享有重大实践语句,或者用ASSERT修改也VERIFY. b..
检查”#ifdef _DEBUG” 内所有代码,排除Release模式采用的代码. c..
查找TRACE 排除其中的有重点履语句.
TRACE和ASSERT一样,仅以Debug模式下编译.
如果由此者修改更凑巧了若在Debug模式下的题目,那么你可另行编译Release模式,非常有或你可以解决先前设有的题目!.
错误的假设造成编译模式错误你是不是经常性的比方你的变量或者目标被初试化成有指定的价(可能0)?您是否要你持有关乎到的资源在应用程序中还在?这些也是Debug和Release模式下不同问题发生的因。

规则 4:

  除非您于代码中针对变量进行初始化,否则不能够作出如齐假定.
包括全局变量,自动变量,申请对象与new对象.
这种情形尚经常来在内存顺序的题目,记得原来采用结构体的时候以使用方便,比较有限只结构体对象下memcmp,在Debug版本工作例行,而Release版本计算产生错误的清除,看来确实不可知展开不当的设!

规则 5:

  确保删除资源的保有援都让删,例如resource.h中之定义.
软件开发中,不同编译版本对变量和内存的初始化是殊之.
如果您要是变量初始化为0,那么在Win9x系统的Release模式下,会出现异常现象。因此对富有变量,内存显式清0是比较安全之召开法.
如果你引用了曾经给剔除的资源,您的Debug版本可以健康干活,但是Release版本可能会见crash.
您是不是相信编译器? 编译器警告级别与编译噪音有相当深的关系.
通过增强编译器警告级别可加程序隐藏问题暴露的机会.通常设置警示级别在”Level
3″或者 “Level
4”.编译并解决有警告,这是发布Release版本应用程序的一个杀好的建议.这会暴露会使您的应用程序出现问题之众初始化问题及其它潜在的错误.

规则 6:

  开始项目事先先拿编译警告级别设置在”Level 3″ 或者 “Level 4”
,登记代码之前确保消灭所有警告!.
总结报告编译模式下的调剂曾经不止一次的闻部分VC开发者说Release模式下不克拓展调节,幸运的凡:通过相应设置,可以于Release模式进行调剂,因此那只不过是一个盖勒索传讹的错说法而已。

 规则 7:

  当前面有的办法都没用的上,在Release模式下进行调试.
Release模式可以拓展调节,第一步是开拓符号表: a..
Alt-F7打开项目安装,在C++/C 页面,选择”General”类,修改Debug Info
setting 为 “Program Database”. b.. 在”Link” 页面,选择”Generate Debug
Info”. c.. “Rebuild All”
这些设置以允许你在Release模式下封存符号表,您为得以又考虑以下设置: a..
调试Release版本应用程序,您可以关闭优化增选项. b..
如果当Release模式下不克设置断点,添加指令”__asm {int 3}”
可以使你的应用程序在该行已(确定在宣布应用程序时候解除这些代码).
在Release模式开展调节之几只限制. a..
最要命之题材在于你不能够跟至MFC函数内部,原因在Release版本的MFC动态链接库不分包调试信息和符号表.
b.. 同齐,想要调节调用的dll,您要叫它们整个添加调试信息和记表.
编译器生成了错误的代码?
或许有的上你见面意识VC++编译器生成了’问题代码’,然而坦率的道,人们常见抱怨的极度早.您可于Release模式下关闭优化增选来拓展测试.
如果是操作解决了您的问题,或许你的编码习惯在问题. 信不信仰由而,
极其可能当您的编码中设有模棱两可的求解或者拘留起似乎是,某些规则下吧是科学的情况.
举个例,下面的代码在Debug模式像一切’正常’,而于Release模式下却会出错!
#include int* func1(){int retval = 5;return &retval;} int main(int
argc, char* argv[]){printf(“%d/n”, *func1());return
0;}我信任大部分程序员尤其是初大方容易碰到此类情况的。

规则 8:

  如果关闭Release模式的优化增选可以使您的应用程序运行正常,而开辟优化增选则产出问题之化,原因多半在你的糟糕编码习惯造成的.
这表示要仔细检查你的代码,清理出那些错误的若,悬空指针等等.
等同的立刻告诉你,在Debug模式与关闭优化增选的Release模式下而的应用程序工作例行全是以系统包含的造化,您要着手更凑巧有隐患的代码,否则在以后恐会见导致巨大的损失。

 规则 9:

  如果你已经彻底反省了您的代码,并且没有发现题目,那么你最好逐个打开优化增选将时有发生错误的来头限制于某个范围之内.
BTW- 以上问题代码由C++编译器自动检出. 如果你已经以 规则 6
您可能在前头环节遇早已解决了这些问题.
凭自身之开销经历,编译器极少会生错误的代码(当然要顾接口程序边界对同的题材).通常在运用模板类时候VC6编译器或许会来断言ASSERT错误,这种状况你就需要创新补丁即可缓解。

     
最后之琢磨在普通编码中只有需要稍微加某些严厉的检测,便能够使得的避免新的Debug
-v- Release模式问题之来,以下是我的片历。

     1. 取出(check out)需要修改的代码。

     2. 改动代码,排除有警告,编译Debug和Release版本.

     3.
详细测试新代码,即单步调试新替代码段之后入工作代码,确保代码无误.

     4. 更凑巧有问题.

     5. 确认对后以新代码登记入库(check in).

     6.
对注册入库的代码进行新的编译,确保新注册代码和另外代码融合.

     7. 双重详细测试代码.

     8. 又凑巧新题材(或许可以窥见报入库代码有的题材)
严格按照上述步骤,您于计划开过程遭到即可解决大气题材,避免以结尾颁发应用程序时候来新的难以稳定的问题.

  后记本文是以自己之开发进程中遇到Release版本应用程序发布,产生错误的早晚苦苦求索得到的一部分历,原文来自于codeproject,经过自身润色,改写成为可国内开发者的文章,希望能够针对大家有因此,谢谢!
转载:
Debug版本包括调试信息,所以若比Release版本大很多(可能很数百K至数M)。至于是否要DLL支持,主要关押你下的编译选项。如果是依据ATL的,则Debug和Release版本对DLL的要求多。如果下的编译选项也下MFC动态库,则需要MFC42D.DLL等库支持,而Release版本需要MFC42.DLL支持。Release
Build不针对源代码进行调节,不考虑MFC的诊断宏,使用的凡MFC
Release库,编译十对应用程序的快慢进行优化,而Debug
Build则正相反,它同意对源代码进行调试,可以定义和应用MFC的诊断宏,采用MFC
Debug库,对快没有优化。

  • 平等、Debug 和 Release 编译方式的本质区别 Debug
    通常号称调试版本,它包含调试信息,并且不作其它优化,便于程序员调试程序。Release
    称为发布版,它数是开展了各种优化,使得程序于代码大小以及运转速度高达都是极其出彩的,以便用户大好地采用。
    Debug 和 Release
    的审秘密,在于一组编译选项。下面列有了分别指向双方的选项(当然除此之外还时有发生另一些,如/Fd
    /Fo,但别并无根本,通常他们也未会见滋生 Release 版错误,在此不讨论)
    Debug 版本: /MDd /MLd 或 /MTd 使用 Debug runtime
    library(调试版本的运作时刻函数库) /Od 关闭优化开关 /D “_DEBUG”
    相当于 #define _DEBUG,打开编译调试代码开关(主要针对 assert函数)
    /ZI 创建 Edit and continue(编辑继续)数据库,这样在调节了
    程中如改动了自代码不需更编译 /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
      时无给编译。除此之外,VC++中还有一样密密麻麻断言宏。这包: ANSI C
      断言 void assert(int expression ); C Runtime Lib 断言 _ASSERT(
      booleanExpression ); _ASSERTE( booleanExpression ); MFC 断言
      ASSERT( booleanExpression ); VERIFY( booleanExpression );
      ASSERT_VALID( pObject ); ASSERT_KINDOF( classname, pobject );
      ATL 断言 ATLASSERT( booleanExpression ); 此外,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 ( Dead
      Data ) 填充已被放走的积聚内存(例如 delete ),0xFD( deFencde Data
      ) 初始化受保障的内存(debug
      版在动态分配内存的光景在护内存以防止越界访问),其中括号中之歌词是微软建议的助记词。这样做的补益是这些价值都蛮要命,作为指针是免容许的(而且
      32
      位系统面临指针很少是惊奇数值,在有点系统遭到奇数的指针会出运行时左),作为数值也很少遇到,而且这些价值也甚易辨认,因此这老便宜于
      Debug 版中发现 Release
      版才会赶上的荒谬。要特别注意的凡,很多人数以为编译器会用 0
      来初始化变量,这是错的(而且这么充分无便于查找错误)。 (2)
      通过函数指针调用函数时,会透过检查栈指针验证函数调用的匹配性。(防止原形不配合) (3)
      函数回前检查栈指针,确认未为修改。(防止越界访问和精神不配合,与第二桩合在一起可大约模拟帧指针省略
      FPO ) 通常 /GZ 选项会造成 Debug 版出错而 Release
      版正常的情景,因为 Release
      版中无初始化的变量是自由的,这起或使指针指向一个得力地址而掩盖了黑访问。
      除此之外,/Gm /GF
      等选项造成错误的景况较少,而且她们之成效明摆着,比较好觉察。
      在动用VC开发软件的长河被,正当要享用那种兴奋的时候猛然意识:
      release与debug运行结果不相同,甚至出错,而release又未便宜调试,真的是一头一高啊,可是疼归疼,问题总要化解,下面用讲述一下自之个别点更,看看是勿是里某: 1.
      变量。大家都晓得,debug跟release在初始化变量时所举行的操作是不同之,debug是用每个字节位都给成0xcc,而release的赋值近似于自由(我怀念是直接从内存中分配的,没有初始化过,但debug为什么不是0xff或0x00或其它什么的就不得而知了)。这样即使明白了,如果你的次第中之之一变量没被初始化就为引用,就挺有或出现异常:用作控制变量将致流程导向不等同;用作数组下标将会见如程序崩溃;更加可能是导致任何变量的禁止确如引起其他的谬误。所以于声明变量后旋即对其初始化一个默认的价值是无限简单易行实用的计,否则路非常了公追寻都并未地方找找。代码有错误在debug方式下或者会见忽略而未深受发觉到,如debug方式下数组越界也大半不会见出错,在release中尽管展露出了,这个找起来便于难了:(
      还是友好多加留意吧 2. 自定义信息的消息参数。
      MFC为咱提供了十分好之信机制,更平添了于定义信息,好处我便不用多说了。这为在debug跟release的题目呢?答案是必的。在自定义信息之函数体声明时,时常会相如此的写法:afx_msg
      LRESULT OnMessageOwn();
      当你于差不多线程或进程中以了消息传递时就见面促成无效句柄之类的不当。这个缘故就是是消息体的参数没有添加,即该写成:afx_msg
      LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam);
      (具体原因我耶非绝知道,是未是坐调用时本在默认的参数多分配了WPARAM+LPARAM的空中要损坏了应用程序的内存空间?还求权威多点)
      避免的办法: 1. 留意变量的初始化 2. 从定义音之专业写法 3.
      施用调试语句TRACE等时行使后极好注释掉 4. 不择手段以try –
      catch(…) VERIFY 和 ASSERT
      的别:一个当Release下面可以尽,一个请勿得以 DEBUG和RELEASE
      版本差异和调试相关题材: 内存分配问题 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版中buffer的NULL覆盖了counter的上位,但是除非counter>16M,什么问题也从没。但是当release版中,counter可能被在寄存器中,这样NULL就挂了buffer下面的半空中,可能就是是函数的归地址,这将招致ACCESS
      ERROR。 3. DEBUG版和RELEASE版的内存分配方式是差之
      。如果你在DEBUG版中申请 ele 为
      6*sizeof(DWORD)=24bytes,实际上分配为你的凡32bytes(debug版以32bytes吗单位分配),
      而在release版,分配受你的哪怕是24bytes(release版以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.休可知用debug和release版的DLL混合在一起使用。debug都是debug版,release版都是release版。
      解决办法是以debug和release的顺序分别位居主程序的debug和release目录下
      2.千万并非认为静态连接库会解决问题,那只是见面要事态重新不好。 VI.
      RELEASE板中之调试 : 1.以ASSERT() 改吗 VERIFY()
      。找来概念在”#ifdef
      _DEBUG”中的代码,如果在RELEASE版本中要这些代码请用他们变到定义外。查找TRACE(…)中代码,因为这些代码在RELEASE中吗不被编译。
      请认真反省那些当RELEASE中需之代码是否连无让便宜。
      2.变量底初始化所带动的两样,在不同的系统,或是在DEBUG/RELEASE版本间都设有这么的异样,所以恳请对变量进行初始化。
      3.是否当编译时都来矣警戒?请将警告级别设置为3要么4,然后保证在编译时没有警示来现. VII.
      将Project Settings” 中 “C++/C ”
      项目下优化增选改也Disbale(Debug)。编译器的优化可能引致众多竟的失实,请参见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的同样卖技术文档中标明,在VC5中对DLL的”Maximize
      Speed”优化增选并未受统统支持,因此就将会晤挑起内存错误并造成程序崩溃

相关文章