release和debug版本(编译器debug和release)

健康新闻 2023-05-01 10:07健康生活www.xingbingw.cn

  release和debug版本(编译器debug和release),新营销网红网本栏目通过数据整理汇集了release和debug版本(编译器debug和release)相关信息,下面一起看看。

  我们知道编译时可以有不同的编译选项和组合。在编译器中,有两种编译选项的组合,即发布和调试。编译时,选择发布或调试,编译后的程序分别称为发布版本或调试版本。前者优化更多,文件更小,后者因为调试的需要,文件更大。当然,无论是release选项还是debug选项,都可以在工程设置中修改一些编译选项,以获得优化的调试版本或带有trace语句的发布版本。

  调试:/MDd /MLd或/MTd使用调试运行时库/Od关闭优化开关。/D _DEBUG等效于#define _DEBUG。打开编译调试代码开关(主要针对assert函数)/ZI创建编辑继续数据库,这样如果在调试过程中修改了源代码,就不需要重新编译了。/GZ可以帮助捕捉内存错误。/Gm打开最小化重新链接开关以减少链接时间。

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

  调试版本包含调试信息,所以比发布版本大很多(可能几百K到几M)。至于是否需要DLL支持,主要看你采用的编译选项。如果是基于ATL的,那么调试版和发布版对dll的要求是类似的。如果编译选项是MFC动态库,则需要MFC42D.DLL等库的支持,而发布版本则需要MFC42.DLL的支持。

   Release不调试源代码,不考虑MFC的诊断宏。相反,它使用MFC发布库,在编译期间优化应用程序的速度。另一方面,Debug Build允许对源代码进行调试,并且可以定义和使用MFC的诊断宏。采用MFC调试库,不优化速度。

  调试和发布编译选项的组合以及某些编译选项的具体差异和细节:

   1链接到哪个运行时库通常只会影响程序的性能。运行时库的调试版本包含调试信息,并采用一些保护机制来帮助发现错误,因此其性能不如发布版本。编译器提供的运行时库通常是稳定的,不会导致发布版本错误;但是由于Debug的运行时库加强了对错误的检测,比如堆内存分配,有时候会出现Debug有错误但是正常释放的情况。需要指出的是,如果调试错了,即使发布正常,程序肯定有Bug,但可能是发布版本的某次运行没有显示出来。

   2优化这是产生错误的主要原因,因为关闭优化时,基本上是直接翻译源程序,而打开优化时,编译器会做一系列假设。主要有以下几种错误:

   2.1框架指针省略(简称FPO):在函数调用过程中,所有的调用信息(返回地址、参数)和自动变量都放在堆栈上。如果函数的声明与实现(参数、返回值、调用方法)不同,就会产生错误。然而,在调试模式下,堆栈访问是通过存储在EBP寄存器中的地址实现的。如果没有数组越界(或“不多”越界)等错误,函数通常可以正常执行;在Release模式下,优化会省略EBP堆栈基址指针,这样通过全局指针访问堆栈会导致返回地址错误,使程序崩溃。c的强类型特性可以检查出这些错误中的大部分,但是如果使用强制类型转换就做不到了。可以在发布版本中强制使用/Oy- compile选项关闭帧指针省略,从而判断是否存在这种错误。

   char * pj path(){ char * path=new char[66]; getcurrentdirectory(66,路径);CStringcpath=pathcpath=test.txt returncpath。get buffer(0);//发布版//可以,调试版是一个随机值}2.2 volatile变量:volatile告诉编译器这个变量可能被程序外的未知方式(比如系统、其他进程、线程)修改。为了提高程序性能,优化程序往往会将一些变量放在寄存器中(类似于register关键字),而其他进程只能修改变量所在的内存,而寄存器中的值保持不变。如果你的程序是多线程的,或者你发现一个变量的值与你期望的不同,并且你确定它已经被正确设置,你很可能会遇到这样的问题。这种错误有时表明程序在最快优化时出错,而最小优化是正常的。尝试在你认为可疑的变量中加入volatile。

   2.3变量优化:优化器将根据变量的用途优化变量。比如函数中有一个未使用的变量,在调试版本中可能会掩盖一个数组越界,而在发布版本中,这个变量很可能会被优化,越界的数组会破坏堆栈中有用的数据。当然,实际情况会比这复杂得多。与此相关的错误有:

  非法访问,包括数组越界、指针错误等。例如:

   void fn(void){ inti;I=1;inta[4];{ intjj=1;} a[-1]=1;//当然误差不会那么明显,比如

  下标是变量a[4]=1;}

  j 虽然在数组越界时已出了作用域,但其空间并未收回,因而 i 和 j 就会掩盖越界。而 Release 版由于 i、j 并未使用可能会被优化掉,从而使栈被破坏。

  如:

  charbuffer[10];intcounter;lstrcpy(buffer, abcdefghik

  在debug版中buffer的NULL覆盖了counter的高位,但是除非counter 16M,什么问题也没有。但是在release版中,counter可能被放在寄存器中,这样NULL就覆盖了buffer下面的空间,可能就是函数的返回地址,这将导致ACCESS ERROR。

  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 之类的条件编译,也要注意一下。

  ASSERT宏是这样定义的:

  #ifdef_DEBUG#defineASSERT(x)if((x)==0)report_assert_failure()#else#defineASSERT(x)#endif

  实际上复杂一些,但无关紧要。假如你在这些语句中加了程序中必须要有的代码 比如

  ASSERT(pNewObj=newCMyClass);pNewObj- MyFunction();

  这种时候Release版本中的pNewObj不会分配到空间 所以执行到下一个语句的时候程序会报该程序执行了非法操作的错误。这时可以用VERIFY :

  #ifdef_DEBUG#defineVERIFY(x)if((x)==0)report_assert_failure()#else#defineVERIFY(x)(x)#endif

  这样的话,代码在release版中就可以执行了。

  需要注意的是 VERIFY()这个宏允许你将程序代码放在布尔表达式里,这个宏通常是用来检查 Windows API 的返回值。有些人可能为这个原因而滥用 VERIFY() ,事实上这是危险的,因为 VERIFY() 违反了断言的思想,不能使程序代码和调试代码完全分离,最终可能会带来很多麻烦。因此,专家们建议尽量少用这个宏。

  4 /GZ 选项

  GZ 选项主要包括:

  4.1 初始化内存和变量。包括用 0xCC 初始化所有自动变量,0xCD ( Cleared Data ) 初始化堆中分配的内存(即动态分配的内存,例如 new ),0xDD ( Dead Data ) 填充已被释放的堆内存(例如 delete ),0xFD( deFencde Data ) 初始化受保护的内存(debug 版在动态分配内存的前后加入保护内存以防止越界访问)。这样做的好处是这些值都很大,作为指针是不可能的(而且 32 位系统中指针很少是奇数值,在有些系统中奇数的指针会产生运行时错误),作为数值也很少遇到,而且这些值也很容易辨认,因此这很有利于在 Debug 版中发现 Release 版才会遇到的错误。要特别注意的是,很多人认为编译器会用 0 来初始化变量,这是错误的(而且这样很不利于查找错误)。

  下面的一段代码在debug中运行的很好,而在release中却不行,因为debug中会自动给变量初始化found=FALSE,而在release版中则不会:

  thing*search(thing*something);BOOLfound;for(inti=0;i whatever.GetSize();i++){if(whatever[i]- field==something- field){/*foundit*/found=TRUE;break;}/*foundit*

Copyright@2015-2025 www.xingbingw.cn 性病网版板所有