zoukankan      html  css  js  c++  java
  • debug VS release

       可以这么说:release版本取掉了debug信息,进行了优化。

       举个例子:那断言----Assertion来说,很多库函数本身的实现代码里,包含了很多的断言。通过ASSERT宏来声明这些断言,这个宏只有在程序的debug版里才有效。而在release版里它完全被忽略,为的也就是减少系统的消耗,至于debug版本运行结果正确,可release版却出错,可能的问题也就在这些断言里,特别是自己写的断言。

       比如ASSERT(***)这***里不光是判断,如果还有其他创建之类的操作,到了release版,被忽略后,也就容易带来问题。举个例子: int a; debug时a值恒定,总为cdcdcdcdrelease时a值不定,为当时内存值这时你在使用a时就会引起结果的不一致,甚至导致溢出!所以对变量初始化尤其是对指针,数组变量初始化是一个良好的习惯!debug与release的不同大多是因为程序代码写的不规范,不严谨而造成的。

     关于Debug和Release之本质区别的讨论~~(主要针对VC6.0和VS2003.net编译工具而言)
    一、Debug 和 Release 编译方式的本质区别

        Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小和运行速度上都是最优的,以便用户很好地使用。
        Debug 和Release的真正秘密,在于一组编译选项。下面列出了分别针对二者的选项(当然除此之外还有其他一些,如/Fd/Fo,但区别并不重要,通常他们也不会引起 Release版错误,在此不讨论)
       
    Debug 版本:
     /MDd /MLd 或/MTd   使用 Debug runtimelibrary(调试版本的运行时刻函数库)
     /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. RuntimeLibrary:链接哪种运行时刻函数库通常只对程序的性能产生影响。调试版本的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_r );
        C RuntimeLib 断言  _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()违反了断言的思想ᦁ ...

  • 相关阅读:
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    使用Jasmine和karma对传统js进行单元测试
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    《.NET内存管理宝典 》(Pro .NET Memory Management) 阅读指南
    nginx 基于IP的多虚拟主机配置
    Shiro 框架的MD5加密算法实现原理
    项目实战:Qt+OSG三维点云引擎(支持原点,缩放,单独轴或者组合多轴拽拖旋转,支持导入点云文件)
    实用技巧:阿里云服务器建立公网物联网服务器(解决阿里云服务器端口,公网连接不上的问题)
  • 原文地址:https://www.cnblogs.com/feisky/p/1586521.html
Copyright © 2011-2022 走看看