深入浅出Debug和Release

一文带你深入浅出VS下的调试技巧 对于一个程序员来说,写代码固然重要,但其实精通调试更重要,本文旨在分享一波VS下调试技巧,以提高防范bug、调试纠错的意识与能力。 阅读详情
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  等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。  
DebugRelease版本区别详解:如何用Android Studio生成最优APK 本文深入解析Android开发中DebugRelease版本的本质区别,涵盖代码优化、调试信息、APK签名及资源处理等核心维度。重点指导开发者如何在Android Studio中配置并生成体积小、性能高且安全的Release版本APK,包括签名管理、R8混淆、资源压缩及构建变体等实战技巧,帮助交付最优应用。 阅读详情

相关推荐

ios逆向工具tweak logos语法总结

文章目录%hook%hook 后面的类名,是要替换的类名, 里面的 self, 就是这个类self 要使用的方法,如果报错找不到方法名,则要在前面声明这个类有这个方法,语法如下:%new %hook %hook 类名 方法1.. 方法2.. %end %hook 后面的类名,是要替换的类名, 里面的 self, 就是这个类 self 要使用的方法,如果报错找不到方法名,则要在前面声明这个类有这个方法,语法如下: @interface 类名 声明的方法 @end 例如make时候报错如下: Twea

小手琴师的博客 1793

DebugRelease的区别是什么(非常详细)?

Debug 版本 Debug 是“调试”的意思,Debug 版本就是为调试而生的,编译器在生成 Debug 版本的程序时会加入调试辅助信息,并且很少会进行优化,程序还是“原汁原味”的。 你没听错,不是任何一个程序都可以调试的,程序中必须包含额外的辅助信息才能调试,否则调试器也无从下手。 Release 版本 Release 是“发行”的意思,Release 版本就是最终交给用户的程序,编译器会使尽浑身解数对它进行优化,以提高执行效率,虽然最终的运行结果仍然是我们期望的,但底层的执行流程可能已经改变了。

aa804738534的博客 4167

CCSDebugRelease的区别

CCSDebugRelease的区别.doc

ReleaseDebug的区别

ReleaseDebug的区别 文章目录ReleaseDebug的区别1. debug release 版本有什么不同1.1 在VS中的区别2. gcc 下 release 版本与 debug 版本的控制 1. debug release 版本有什么不同 DebugDebug 通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发人员提供 ...

一叶知秋的博客 3万+

releasedebug到底有什么区别,为什么项目都用的release

Debug模式下,代码通常不会进行优化,以便开发人员能够更轻松地调试诊断代码问题。此外,Debug模式还会生成额外的调试信息,例如变量名称、行号等,以便在出现错误时能够更容易地定位问题。在生产环境中,性能效率往往是关键因素,因此使用Release模式可以优化代码,提高运行速度,并减少可执行文件的大小。在开发过程中,通常会在Debug模式下进行编码测试,以便能够更方便地进行调试故障排除。一旦开发测试完成,并准备好进行部署,就会使用Release模式来构建最终的可执行文件。

weixin_50531046的博客 3905

VS实用调试技巧

bug本意是“昆⾍”或“⾍⼦”,现在⼀般是指在电脑系统或程序中,隐藏着的⼀些未被发现的缺陷或问题,简称程序漏洞。“Bug” 的创始⼈格蕾丝·赫柏(Grace Murray Hopper),她是⼀位为美国海军⼯作的电脑专家,1947年9⽉9⽇,格蕾丝·赫柏对Harvard Mark II设置好17000个继电器进⾏编程后,技术⼈员正在进⾏整机运⾏时,它突然停⽌了⼯作。

hdxbufcbjuhg的博客 1170

C语言学习|bug调试优化代码

C语言中,调试的技巧减少程序错误的技巧

DADONGOOO的博客 590

P32调试技巧1、2

第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误Debug通常称为调试版本,它包含调试信息,并且不作任何优化,便于程序员调试程序。Release称为发布版本,它往往是进行了各种优化,使得程序在代码大小运行速度上都是最优的,以便用户很好地使用。

安全小白,希望我的文章对你有所帮助。 1005

笔记17(C语言 实用调试技巧)

什么是bug? ps:第一次被发现的导致计算机错误的飞蛾,也是第一个计算机程序错误 调试是什么?调试的重要性? ||| 调试(英语:Debugging / Debug),又称除错,是发现减少计算机程序电子仪器设备中程序错误的一个过程。 调试的基本步骤 发现程序错误的存在 ( 1.程序员自己解决 2.测试人员测试软件 3.用户反馈 - 严重) 以隔离、消除等方式对错误进行定位 确定错误发生的原因 提出纠正错误的解决办法 对程序错误予以改正,重新测试 DebugRel...

w_pab的博客 2120

开源项目推荐:release模式正常debug模式下报错问题分析文档

开源项目推荐:release模式正常debug模式下报错问题分析文档 【下载地址】release模式正常debug模式下报错问题分析文档 该项目深入探讨了编程开发中一个常见但棘手的问题:代码在Debug模式下运行正常,而在Release模式下却出现错误。通过一个具体案例,文档详细分析了问题根源,并提供了实用的解决思路。它...

gitblog_06789的博客 980

android-gradle-深入浅出-五:build type

使用Build Types来配置android gradle项目

大头鬼Bruce 2万+

深入浅出微信公众平台实战开发

一套代码达到以下效果: 打包不同applicationId能同时安装在同一手机上 不同logo,app名称, 不同第三方SDK接入配置(例如微信分享appid,激光推送appkey) 能区分debugrelease配置 使用到的功能:productFlavorbuildTypes 原理:优先级buildTypes大于productFlavor 示例:一套代码为两家银行打包apk 1. 修改build.gradle。buildTypes保持默认debugrelease两种设置即可,andr

weixin_46240930的博客 239

深入浅出Android Gradle构建系统(五:build type)

默认情况下,Android插件自动为项目构建一个debug一个release版本的应用。这两个版本的不同主要体现在在非开发机上的调试功能以及APK的签名方式。debug版本使用一个用公开的name/password创建的key来签名(这样构建的时候就不需要提示输入密码了)。release版本在构建的时候不会进行签名,而是稍后在做。 这个可以使用gradle中的BuildType对象来进行配置。

blues的博客 781
上一篇: 常用控件中问题总结
下一篇: A few notes about the CommDb (use in Symbian OS)
kchai1
博客等级 码龄24年 43粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值