关于release版本和Debug 版本的问题

xiang1358 2005-06-10 11:53:25
我写了个程序,为什么在Debug版本下没问题,在release版本下会出现问题。请问应该是什么问题啊???
...全文
157 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiang1358 2005-06-12
  • 打赏
  • 举报
回复
多谢各位兄弟
SGUav 2005-06-10
  • 打赏
  • 举报
回复

偶的一次release 版错误调试记录:

问题分析

由于该问题所具有的特点:

1、只有在大数据量操作时才出现;

2、只有当分析网页与下载网页交替操作时才出现;

3、无规律出现"Out of memory"错误消息误导错误分析;

从而导致了调试分析时的复杂程度。在开始时,认为可能是内存空间大量使用导致的内存不够问题,以及 IE 窗口打开网页网页导致内存耗尽。但是,通过大量测试,发现:内存使用较少时会发生错误,而内存使用较多时不发生错误的现象是存在的。因而否定上述看法。

又怀疑是否是因为提取链接时导致错误的发生,通过在相关函数中添加大量 Assert TRACE 等调试代码后,可以初步判定错误的原因并非上述。

通过多次大数据量操作以及详细的操作记录,另外借助 BoundsCheck 的检查,得到了发生错误时的 BoundsCheck 记录,通过分析该记录,发现发生错误时 BoundsCheck 确定的代码行多集中于 CRecordFile.cpp 中,特别是在该类的析构函数中。通过对该类实现文件中各个分配使用内存的环节添加调试代码,观察其指针及内存使用情况,发现,当关闭程序时,在析构函数中有时会出现两次析构的现象,经多次运行,进一步发现该现象发生在打开网页设置对话框的情况下,于是,可以推断,CRecordFile 在运行过程中产生了两个对象。经过模拟,发现重复使用 free(p) 释放内存,会发生与程序中一模一样的错误。因此可以初步断定错误的发生可能与产生的两个对象有关。

修改程序,使整个程序中只使用一个 CRecordFile 对象,通过大量对照发生错误时的操作成的的测试,均未发现同样错误出现。目前可以认为,问题得到了解决。
SGUav 2005-06-10
  • 打赏
  • 举报
回复
release 版错误是很头疼的问题,造成release 错误可能会有多种原因,从我个人体会来看,感觉Debug 版的容错性好像比realse 版强,但这对我们并非好事。楼上说得很全面,关键还是要掌握错误出现的规律,能够重现错误,确定错误发生的位置,如果有必要的话,进行Release 下的单步跟踪。有的情况下,使用BcCheck也能提供一些线索。
bohut 2005-06-10
  • 打赏
  • 举报
回复
这个可能更全面:调试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模式,观察错误是否产生,更改编译预定义符号方法如下:

Alt-F7打开项目设置,在C++/C 页面,选择"General"类别,更改"_DEBUG"符号为"NDEBUG".
在C++/C 页面, 选择"Preprocessor"类别,添加预定义符号"_DEBUG"到"Undefined Symbols"栏.
使用"Rebuild All"重新编译
如果通过上面设置,您在Release编译模式下面的问题在Debug模式下重现,那么请您依据以下步骤对您的代码进行修改:

查找ASSERT排除其中的所有重要执行语句,或者将ASSERT修改为VERIFY.
检查"#ifdef _DEBUG" 内所有代码,排除Release模式使用的代码.
查找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模式可以进行调试,第一步是打开符号表:

Alt-F7打开项目设置,在C++/C 页面,选择"General"类,修改Debug Info setting 为 "Program Database".
在"Link" 页面,选择"Generate Debug Info".
"Rebuild All"
这些设置将允许您在Release模式下保留符号表,您也可以同时考虑以下设置:

调试Release版本应用程序,您可以关闭优化选项.
如果在Release模式下面不能设置断点,添加指令"__asm {int 3}" 可以是您的应用程序在改行停止(确定在发布应用程序时候排除这些代码).
在Release模式进行调试的几个限制.

最大的问题在于您不能跟踪到MFC函数内部,原因在于Release版本的MFC动态链接库不包含调试信息和符号表.
同上,想要调试调用的dll,您必须给它们全部加上调试信息和符号表.
编译器生成了错误的代码?
或许有的时候您会发现VC++编译器生成了’问题代码’,然而坦率的讲,人们通常抱怨的太早.您可以在Release模式下面关闭优化选项来进行测试.

如果这个操作解决了您的问题,或许您的编码习惯存在问题. 信不信由你, 极其可能在您的编码中存在模棱两可的求解或者看起来似乎正确,某些条件下也是正确的情况. 举个例子,下面的代码在Debug模式似乎一切’正常’,而在Release模式下面却会出错!

#include <stdio.h>

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,经过本人润色,改写成为适合国内开发者的文章,希望能对大家有用,谢谢!
bohut 2005-06-10
  • 打赏
  • 举报
回复
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 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项,从而得到优化过的调试版本或是带跟踪语句的发布版本。
qrlvls 2005-06-10
  • 打赏
  • 举报
回复
分段屏蔽来检测出错位置
vcmute 2005-06-10
  • 打赏
  • 举报
回复
ASSERT中不要搁操作,只需判断
wshcdr 2005-06-10
  • 打赏
  • 举报
回复
很可能是指针用错
38062708 2005-06-10
  • 打赏
  • 举报
回复
我曾经也遇到过类似问题,我当时的问题是CTabCtrl的mask没有初始化,因为在 DEBUG 和 RELEASE 下mask的初始值不一样,所以在 DEBUG 下运行没问题,在RELEASE 下运行就出错。楼主看看你的程序中有无类似问题

16,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧