百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

release与debug版编译选项组合差异及不一致的情形

cac55 2024-09-20 13:02 21 浏览 0 评论

我们知道,编译时可以有不同的编译选项及组合。在编译器中,有两种编译选项组合,分别是release与debug,编译时,选择release或者debug,编译出来的程序分别称为release版或者debug版,前者优化较多,文件较小,后者因为调试的需要,文件较大。当然,不管是releas选项,还是debug选项,其中的一些编译选项可以在工程设置中做修改,从而得到优化过的调试版本或是带跟踪语句的发布版本。

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版本大很多(可能大数百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版的的编译选项组合及一些编译选项的具体差异和细节:

1 Runtime Library

链接哪一种运行时刻函数库通常只对程序的性能产生影响。调试版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,因此性能不如发布版本。编译器提供的 Runtime Library 通常很稳定,不会造成 Release 版错误;倒是由于 Debug 的 Runtime Library 加强了对错误的检测,如堆内存分配,有时会出现 Debug 有错但 Release 正常的现象。应当指出的是,如果 Debug 有错,即使 Release 正常,程序肯定是有 Bug 的,只不过可能是 Release 版的某次运行没有表现出来而已。

2 优化

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

2.1 帧指针(Frame Pointer)省略(简称 FPO ):在函数调用过程中,所有调用信息(返回地址、参数)以及自动变量都是放在栈中的。若函数的声明与实现不同(参数、返回值、调用方式),就会产生错误。但 Debug 方式下,栈的访问通过 EBP 寄存器保存的地址实现,如果没有发生数组越界之类的错误(或是越界“不多”),函数通常能正常执行;Release 方式下,优化会省略 EBP 栈基址指针,这样通过一个全局指针访问栈就会造成返回地址错误使程序崩溃。C++ 的强类型特性能检查出大多数这样的错误,但如果用了强制类型转换,就不行了。你可以在 Release 版本中强制加入 /Oy- 编译选项来关掉帧指针省略,以确定是否此类错误。

char* pjPath()
{
	char * path = new char[66];
	::GetCurrentDirectory(66,path);
	CString cpath = path;
    cpath += "test.txt";
	return cpath.GetBuffer(0);// release版OK,debug版是随机值
}

2.2 volatile 型变量:volatile 告诉编译器该变量可能被程序之外的未知方式修改(如系统、其他进程和线程)。优化程序为了使程序性能提高,常把一些变量放在寄存器中(类似于 register 关键字),而其他进程只能对该变量所在的内存进行修改,而寄存器中的值没变。如果你的程序是多线程的,或者你发现某个变量的值与预期的不符而你确信已正确的设置了,则很可能遇到这样的问题。这种错误有时会表现为程序在最快优化出错而最小优化正常。把你认为可疑的变量加上 volatile 试试。

2.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 并未使用可能会被优化掉,从而使栈被破坏。

如:

char buffer[10]; 
int counter; 
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 
#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版中就可以执行了。

需要注意的是 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); 
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; 

所以尽可能的给变量、类或结构初始化。

4.2 通过函数指针调用函数时,会通过检查栈指针验证函数调用的匹配性(防止原形不匹配)。

4.3 函数返回前检查栈指针,确认未被修改(防止越界访问和原形不匹配,与第二项合在一起可大致模拟帧指针省略 FPO )。

通常 /GZ 选项会造成 Debug 版出错而 Release 版正常的现象,因为 Release 版中未初始化的变量是随机的,这有可能使指针指向一个有效地址而掩盖了非法访问。 除此之外,/Gm /GF 等选项造成错误的情况比较少,而且他们的效果显而易见,比较容易发现。


5 Debug版OK,Release版异常的情形

5.1 数组越界

    CTime m_day = CTime::GetCurrentTime();
    CString week = m_day.Format("%w");
  
    int i = atoi(week.GetBuffer(0)); // 当星期天时i=0
    //CString weeks = _T("一二三四五六七");
    //weeks = weeks.Mid(i*2-2,2);
    CString weeks[] = {"Sun.","Mon.", "Tue.", "Wed.", "Thu.", "Fri.", "Sat."};
    CString str = "hi,\r";
    str += "" + m_day.Format("%Y-%m-%d " + weeks[i-1]);//当星期天时i=0,改为weeks[i-1]

另外需要注意的是,规模大一点的程序与玩具程序的错误是有所区别的,原因在于栈上数据的覆盖,及内存地址篡改的概率不一致。

5.2 自定义消息的参数

MFC为我们提供了很好的消息机制,更增加了自定义消息,好处我就不用多说了。这也存在debug跟release的问题吗?答案是肯定的。在自定义消息的函数体声明时,时常会看到这样的写法:afx_msg LRESULT OnMessageOwn(); 当你在多线程或进程间使用了消息传递时就会导致无效句柄之类的错误。这个原因就是消息体的参数没有添加,即应该写成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); 否则Debug会过,而Release出错。

6 Debug版异常,Release版OK的情形

6.1 内存泄漏

pDC=GetDC();
.......
ReleaseDC(pDC); //正确!不能忘记,不能是pDC->DeleateDC(); 

要养成结对编程的习惯。

6.2 指针的初始化

    //CTime m_tQueryTime;
    SYSTEMTIME* m_tQueryTime; // 未初始化
    m_dayCtrl.GetCurSel(m_tQueryTime); // m_dayCtrl是日历控件的control变量
   // 更改为以下即可:
    SYSTEMTIME m_tQueryTime;
    m_dayCtrl.GetCurSel(&m_tQueryTime);

6.3 申请动态内存与实际需要内存的不一致

         // 写入UTF-8的BOM文件头
        char header[3] = {(char)0xEF, (char)0xBB, (char)0xBF};
        fwrite(header, sizeof(char), 3, fp);
        char *chs = (char *)m_textblock.GetBuffer(0);
        int k = m_textblock.GetLength();
        wchar_t *wc = (wchar_t *)malloc(sizeof(wchar_t)*k+2); // 此处要+2

        // 将ANSI编码的多字节字符串转换成宽字符字符串
        int n = MultiByteToWideChar(CP_ACP, 0, chs, strlen(chs), wc, k);
        if ( n > 0 )
        {
            wc[n] = 0;
            char *mb = (char *)malloc(sizeof(char)*k*4);
            // 将宽字符字符串转换成UTF-8编码的多字节字符串
            n = WideCharToMultiByte(CP_UTF8, 0, wc, wcslen(wc), mb, k*4, NULL, NULL);
            if ( n > 0 )
            {
                mb[n] = 0;
                fwrite(mb, sizeof(char), strlen(mb), fp);
            }
            free(mb);
        }
        
        free(wc); // 如果上面动态内存字节数未+2,
        //会出现DAMAGE: after Normal block(#4213) at 0x的错误

综上,release版和debug版的本质差异在于编译选项的组合不一致,需要在release版和debug版之间交替编译,以及早发现潜在的错误。

-End-

相关推荐

无力吐槽的自动续费(你被自动续费困扰过吗?)

今天因为工作需要,需要在百度文库上下载一篇文章。没办法,确实需要也有必要,只能老老实实的按要求买了个VIP。过去在百度文库上有过类似经历,当时为了写论文买了一个月的VIP,后面也没有太注意,直到第二个...

百度文库推出“文源计划”创作者可一键认领文档

11月7日,百度文库发布了旨在保护创作者权益的“文源计划”。所谓“文源计划”,即为每一篇文档找到源头,让创作者享受更多的权益。据百度文库总经理李小婉介绍,文源计划分为三部分,分别是版权认证、版权扶持和...

有开放大学学号的同学,百度文库高校版可以用了。

还在网上找百度文库的下载方式,只要从身边的朋友在读开放大学的,那他(她)的学号就可以登陆到国家开放大学图书馆,还使用百度文库高校版来下载。与百度文库稍有不同,但足够使用了。现转国图链接如下:htt...

搜索资源方法推荐(搜索资源的方法)

今天msgbox就要教大家如何又快又准的搜到各类资源,第一点,排除干扰百度搜索出来啊经常前排展示它的产品以及百度文库,如何去除呢?很简单,后面输入空格减号百度文库,比如你搜高等数学百度文库很多,只要后...

一行代码搞定百度文库VIP功能(2021百度文库vip账号密码共享)

百度文库作为大家常用查资料找文档的平台,大多数文档我们都可以直接在百度文库找到,然而百度文库也有让人头痛的时候。好不容易找到一篇合适的文档,当你准备复制的时候他却提示你需要开通VIP才能复制~~~下载...

百度文库文档批量上传工具用户说明书

百度文库文档批量上传工具用户说明书1、软件主要功能1、批量上传文档到百度文库,支持上传到收费、VIP专享、优享以及共享。2、支持自动分类和自动获取标签3、支持多用户切换,一个账户传满可以切换到...

百度文库现在都看不到文档是否上传成功,要凉了吗?

打开知识店铺,百度文库文档里显示都是下载这一按键,上传的文档也看不到是否成功?咋情况,要取消了吗?没通过审核的也不让你删除,是几个意思,想通吃吗?现在百度上传文档也很费劲,有时弄了半天的资料上传审核过...

微信推广引流108式:利用百度文库长期分享软文引流

百度文库相对于百度知道、百度百科来说,操作上没那么多条条框框,规则上也相对好把握些。做一条百度知道所花费的精力一般都会比做一条百度文库的要多些,老马个人操作下来觉得百度文库更好把握。但见仁见智吧,今天...

职场“避雷”指南 百度文库推出标准化劳动合同范本

轰轰烈烈的毕业季结束了,众多应届生在经过了“职场海选”后,已正式成为职场生力军的一员。这一阶段,除了熟悉业务,签订劳动合同、了解职场福利也迅速被提上日程。而随着国人法律意识的增强,百度文库内《劳动合同...

《百度文库》:素材精选宝库(百度文库官网首页)

《百度文库》:独特功能助力选择高质量素材在当今信息爆炸的时代,如何高效地获取并利用有价值的素材成为了许多人面临的挑战。而《百度文库》作为百度公司推出的一款在线文档分享平台,凭借其丰富的资源、强大的功能...

深度整合和开放AI能力 百度文库和网盘推出内容操作系统「沧舟OS」

【TechWeb】4月25日消息,Create2025百度AI开发者大会上,百度文库和百度网盘推出全球首个内容操作系统——沧舟OS。基于沧舟OS,百度文库APP全新上线「GenFlow超能搭子」...

女子发现大二作业被百度文库要求付费下载,律师:平台侵权,应赔偿

近日,28岁的黎女士在百度百科搜索家乡的小地名时,发现了自己在大二完成的课题作业。她继续搜索,发现多个平台收录了该文,比如豆丁网和文档之家等,有的还设置了付费或积分下载。2月15日,九派新闻记者以用户...

2016杀入百度文库的新捷径,只有少数人才知道的喔

百度的产品在SEO优化中的分量真不用多说,其实很多人都像我一样一直在找捷径。但是我经常发现很多人都是在用死方法。比如发贴吧发帖而不知道去申请一个吧主,知道自问自答而不知道去申请一个合作资格。口碑和贴吧...

百度文库付费文档搜索方法(百度文库付费文档搜索方法有哪些)

一直以来,百度文库中无论是个人中心还是个人主页,都没有像淘宝一样的店内搜索功能,连最近新开的知识店铺也没有设计店内搜索功能,这无论是对上传用户还是下载用户都不方便,上传用户想要搜索自己的文档无法办到...

供读者免费使用!泰达图书馆机构版百度文库新年上新啦

在泰达图书馆读者使用百度文库数字资源不需要VIP,免-费-用!惊不惊喜?快来了解一下吧……新年伊始,为满足区域企业、高校、科研院所以及居民群众在教学、科研及学习过程中,对各类文献资源的需求,泰达图书馆...

取消回复欢迎 发表评论: