[VC笔记]Debug和Release的困惑
- 格式:doc
- 大小:77.50 KB
- 文档页数:3
vc中debug和release的不同在使用VC开发软件的过程中,正当要享受那种兴奋的时候突然发现:release与debug运行结果不一致,甚至出错,而release又不方便调试,真的是当头一棒啊,可是疼归疼,问题总要解决,下面将讲述一下我的几点经验,看看是不是其中之一:1. 变量。
大家都知道,debug跟release在初始化变量时所做的操作是不同的,debug是将每个字节位都赋成0xcc(注1),而release的赋值近似于随机(我想是直接从内存中分配的,没有初始化过)。
这样就明确了,如果你的程序中的某个变量没被初始化就被引用,就很有可能出现异常:用作控制变量将导致流程导向不一致;用作数组下标将会使程序崩溃;更加可能是造成其他变量的不准确而引起其他的错误。
所以在声明变量后马上对其初始化一个默认的值是最简单有效的办法,否则项目大了你找都没地方找。
代码存在错误在debug方式下可能会忽略而不被察觉到,如debug方式下数组越界也大多不会出错,在release中就暴露出来了,这个找起来就比较难了:( 还是自己多加注意吧2. 自定义消息的消息参数。
MFC为我们提供了很好的消息机制,更增加了自定义消息,好处我就不用多说了。
这也存在debug跟release的问题吗?答案是肯定的。
在自定义消息的函数体声明时,时常会看到这样的写法:afx_msg LRESULT OnMessageOwn(); Debug情况下一般不会有任何问题,而当你在Release下且多线程或进程间使用了消息传递时就会导致无效句柄之类的错误。
导致这个错误直接原因是消息体的参数没有添加,即应该写成:afx_msg LRESULT OnMessageOwn(WPARAM wparam, LPARAM lparam); (注2)3. release模式下不出错,但debug模式下报错。
这种情况下大多也是因为代码书写不正确引起的,查看MFC的源码,可以发现好多ASSERT 的语句(断言),这个宏只是在debug模式下才有效,那么就清楚了,release版不报错是忽略了错误而不是没有错误,这可能存在很大的隐患,因为是Debug模式下,比较方便调试,好好的检查自己的代码,再此就不多说了。
感言:真是久病成良医啊!1. C语言中出现 "Compiling... Error spawning cl.exe是什么意思?怎么解决?很多人在安装VC 6.0后有过点击“Compile或者“Build后被出现的"Compiling... ,Error spawning cl.exe错误提示给郁闷过。
很多人的选择是重装,实际上这个问题很多情况下是由于路径设置的问题引起的,"CL.exe是VC使用真正的编译器(编译程序),其路径在“ VC根目录Bin ”下面,你可以到相应的路径下找到这个应用程序。
因此问题可以按照以下方法解决:打开vc界面点击VC “TOOLS (工具)”一> “Optio n选择)”—> “ Directories(目录)"重新设置“ Excutable Fils Include Files、Library Files、Source Files的路径。
很多情况可能就一个盘符的不同(例如你的VC装在C,但是这些路径全部在D),改过来就OK 了。
如果你是按照初始路径安装VC6.0的,路径应为:executatble files:C:\Program Files'Microsoft Visual Studio'Commo n\MSDev98\Bi nC:\Program Files'Microsoft Visual Studio\VC98\BINC:\Program Files'Microsoft Visual Studio'Commo n'TOOLSC:\Program Files'Microsoft Visual Studio\Commo n\TOOLS\WINNTin clude files:C:\Program Files'Microsoft Visual Studio\VC98\INCLUDEC:\Program Files'Microsoft Visual Studio\VC98\MFC\INCLUDEC:\Program Files'Microsoft Visual Studio\VC98\ATL\INCLUDElibrary files:C:\Program Files'Microsoft Visual Studio\VC98\LIBC:\Program Files'Microsoft Visual Studio\VC98\MFC\LIBsource files:C:\Program Files'Microsoft Visual Studio\VC98\MFC\SRCC:\Program Files'Microsoft Visual Studio\VC98\MFC\INCLUDEC:\Program Files'Microsoft Visual Studio\VC98\ATL\INCLUDEC:\Program Files'Microsoft Visual Studio\VC98\CRT\SRC如果你装在其他盘里,则仿照其路径变通就行。
VC中常见的一些编译链接错误的解决VC中常见的一些编译链接错误的解决2010-03-22 15:59问题1:Linking...nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadexnafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadexlibcd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main答VC++默认的工程设置是单线程的,而你使用了多线程,所以要修改设置。
选择菜单“Project|settings”,选择C/C++标签,在CODEGENERATION分类中选择除SINGLE-THREADED的其他选择。
比如可以在Use run-time library中选择Debug Multithreaded 或者multithreaded其中,Single-Threaded 单线程静态链接库(release版本) Multithreaded 多线程静态链接库(release版本) multithreaded DLL 多线程动态链接库(release版本)Debug Single-Threaded 单线程静态链接库(debug版本)Debug Multithreaded 多线程静态链接库(debug版本)Debug Multithreaded DLL 多线程动态链接库(debug版本)单线程: 不需要多线程调用时, 多用在DOS环境下多线程: 可以并发运行静态库: 直接将库与程序Link, 可以脱离MFC库运行动态库: 需要相应的DLL动态库, 程序才能运行release版本: 正式发布时使用debug版本: 调试阶段使用问题2fatal error C1010: unexpected end of file while looking for precompiled header directive该如何解如果发生错误的文件是由其他的C代码文件添加进入当前工程而引起的,则Alt+F7进入当前工程的Settings,选择C/C++选项卡,从Category组合框中选中Precompiled Headers,选择Not Using Precompiledheaders。
了解调试,首先要知道"断点"这个概念.断点就是程序运行中可能会中断的地方,方便开发者在程序运行的过程中查看程序当前的运行状态,比如变量的值,函数的返回值等等.究竟怎么使用断点呢?1.调试快捷键F9——在当前光标所在的行下断点,如果当前行已经有断点,则取消断点.F5——调试状态运行程序,程序执行到有断点的地方会停下来.F10——单步执行程序.F11——和F10的区别是,如果当前执行语句是函数调用,则会进入函数里面.CTRL+F10——运行到光标所在行.SHIFT+F11——跳出当前所在函数.特别说明:a.有的地方不能下断点.比如空行,基本类型定义语句(不初始化),等等非执行语句.比如int i; // 此行不能下断点字串7int j = 0; // 这里可以下CString str; // 这里可以下int k = Max(i, j); // 这里可以下b.不是所有断点都会(断).比如下断点的语句在程序里面没有被执行.c.此外,ALT+F9还可以下条件断点,不过这个不常用,有兴趣的可以自己研究:)2.如何调试Release版的程序?有些程序在debug下运行的很好,但在release下却总是失败,默认情况下release是不能调试的.怎么解决呢?其实"debug"和"release"都只是一个代号而已,各自对应一组不同的编译选项.在release的默认设置下,ALT+F7 ,调出工程设置对话框,切换到link选项卡,勾选"Generate debug info",然后再切换到C/C++选项卡,在"Optimizations"里面选"Disable(Debug)",在下面的"Debug info"里面选"Program Database for Edit and Continue".然后点OK保存设置,重新编译程序,下断点即可.3.上面两点已经能应付很多种情况了,但是有时候即使在debug下也不能下断点,我以前也曾经遇到过这种问题,一个调了4层的dll,就是不能下断点.其实还除了上面的方法,还有一种最底层的实现技术:使用汇编.细心的人可能会发现,有时候在调试状态下当程序出现异常的时候,光标会指向汇编代码,而这个代码就是int 3,这其实是一个中断.在你的代码的任意地方加上__asm{int 3};用debug编译,然后直接运行程序.当程序执行到上述代码的时候,就会出现一个框,告诉你说一大堆信息,说程序错了,下面有"调试"、"终止"、"忽略",不用理,点调试就可以进入跟踪了。
自己总是用VC平台来开发东西,但是有时候总是出这样那样的问题,在这里把Visual C++开发工具与调试技巧整理摘录如下,希望对大家有用,省去大家再去搜索的烦恼。
1.如何在Release状态下进行调试Project->Setting=>ProjectSetting对话框,选择Release状态。
C/C++标签中的Category选General,Optimizations选Disable(Debug),Debut info选Program Database。
在Link标签中选中Generate debug info复选框。
注:只是一个介乎Debug和Release的中间状态,所有的ASSERT、VERIFY都不起作用,函数调用方式已经是真正的调用,而不查表,但是这种状态下QuickWatch、调用队列跟踪功能仍然有效,和Debug版一样。
2. Release和Debug有什么不同Release版称为发行版,Debug版称为调试版。
Debug中可以单步执行、跟踪等功能,但生成的可执行文件比较大,代码运行速度较慢。
Release版运行速度较快,可执行文件较小,但在其编译条件下无法执行调试功能。
Release的exe文件链接的是标准的MFC DLL(Use MFC in a shared or static dll)。
这些DLL在安装Windows的时候,已经配置,所以这些程序能够在没有安装Visual C++ 6.0的机器上运行。
而Debug版本的exe链接了调试版本的MFC DLL文件,在没有安装Visual C++6.0的机器上不能运行,因为缺相应的DLL,除非选择use static dll when link。
3. ASSERT和VERIFY有什么区别ASSERT里面的内容在Release版本中不编译,VERIFY里面的内容仍然翻译,但不再判断真假。
所以后者更安全一点。
例如ASSERT(file.Open(strFileName))。
VC编译常见错误- -1、Fatal Error C1010unexpected end of file while looking for precompiled header di rective这一般是由于使用了参数/Yu"stdafx.h",意思是在每个文件中都应该使用#include来包含这个头文件。
一般改正,就是在每个。
CPP文件中包含这个文件就可以。
2、LNK2001 on __beginthreadex and __endthreadex这是一个非常常见的链接错误。
原因是由于在VC3。
0以后所有的M FC类都是线程安全的,在MFC类库中使用了Thread Local Stora ge (TLS)提供预处理的数据。
因此如果程序中包含了"stfafx.h"头文件或者使用了,MFC类库中的类就会使用MSVCRTx0.DLL 来进行链接。
改正方法如下:On Visual C 2.x, 5.0, and 6.0:Select the Project menu, and then continue Steps 2 throug h 5 below.On Visual C 4.x:Select the Build menu.Select the Settings... option.Select the C/C++ tab.Select Code Generation on the Category list box.Finally, make a selection other than Single-Threaded on th e Use Run Time Library list box.如果使用了"Multithreaded using DLL"方式还要求在预处理符号中加上_AFXDLL 符号.还在LIBC。
LIB是C Runtime的静态链接库。
Release和Debug的区别Debug与Release版本的区别Debug 和 Release 并没有本质的区别,他们只是VC预定义提供的两组编译选项的集合,编译器只是按照预定的选项⾏动。
如果我们愿意,我们完全可以把Debug和Release的⾏为完全颠倒过来。
当然也可以提供其他的模式,例如⾃⼰定义⼀组编译选项,然后命名为MY_ABC等。
习惯上,我们仍然更愿意使⽤VC已经定义好的名称。
Debug版本包括调试信息,所以要⽐Release版本⼤很多(可能⼤数百K⾄数M)。
⾄于是否需要DLL⽀持,主要看你采⽤的编译选项。
如果是基于 ATL的,则Debug和Release版本对DLL的要求差不多。
如果采⽤的编译选项为使⽤MFC动态库,则需要MFC42D.DLL等库⽀持,⽽ Release版本需要MFC42.DLL⽀持。
Release不对源代码进⾏调试,不考虑MFC的诊断宏,使⽤的是 MFC Release库,编译时对应⽤程序的速度进⾏优化,⽽Debug则正好相反,它允许对源代码进⾏调试,可以定义和使⽤MFC的诊断宏,采⽤MFC Debug库,对速度没有优化。
既然Debug和 Release仅仅是编译选项的不同,那么为什么要区分Debug和Release版本呢?Debug和Release,在我看来主要是针对其⾯向的⽬标不同的⽽进⾏区分的。
Debug通常称为调试版本,通过⼀系列编译选项的配合,编译的结果通常包含调试信息,⽽且不做任何优化,以为开发⼈员提供强⼤的应⽤程序调试能⼒。
⽽Release通常称为发布版本,是为⽤户使⽤的,⼀般客户不允许在发布版本上进⾏调试。
所以不保存调试信息,同时,它往往进⾏了各种优化,以期达到代码最⼩和速度最优。
为⽤户的使⽤提供便利。
下⾯仅就默认的Debug和Release版本的选项进⾏⽐较,详细的编译选项可以看MSDN的说明。
我们将默认的Debug和Release的选项设置进⾏⽐较,过滤掉相同设置,主要的不同如下:编译选项:/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug““" /ZI链接选项:/OUT:"D:“MyProject“logging“Debug“OptionTest.dll" /INCREMENTALRelease设置:编译选项:/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release““" /Zi链接选项:/OUT:"D:“MyProject“logging“Release“OptionTest.dll" /INCREMENTAL:NODebug 版本:/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 合并重复的字符串,并将字符串常量放到只读内存,防⽌被修改MDd与MD⾸先,Debug版本使⽤调试版本的运⾏时库(/MDd选项),Relase版本则使⽤的是发布版本的运⾏时库(vcrt.dll)。
Debug与Release版本的区别Debug 和Release 并没有本质的区别,他们只是VC预定义提供的两组编译选项的集合,编译器只是按照预定的选项行动。
如果我们愿意,我们完全可以把Debug和Release 的行为完全颠倒过来。
当然也可以提供其他的模式,例如自己定义一组编译选项,然后命名为MY_ABC等。
习惯上,我们仍然更愿意使用VC已经定义好的名称。
Debug版本包括调试信息,所以要比Release版本大很多(可能大数百K至数M)。
至于是否需要DLL支持,主要看你采用的编译选项。
如果是基于ATL的,则Debug和Release 版本对DLL的要求差不多。
如果采用的编译选项为使用MFC动态库,则需要MFC42D.DLL 等库支持,而Release版本需要MFC42.DLL支持。
Release不对源代码进行调试,不考虑MFC的诊断宏,使用的是MFC Release库,编译时对应用程序的速度进行优化,而Debug则正好相反,它允许对源代码进行调试,可以定义和使用MFC的诊断宏,采用MFC Debug库,对速度没有优化。
既然Debug和Release仅仅是编译选项的不同,那么为什么要区分Debug和Release 版本呢?Debug和Release,在我看来主要是针对其面向的目标不同的而进行区分的。
Debug通常称为调试版本,通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发人员提供强大的应用程序调试能力。
而Release通常称为发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。
所以不保存调试信息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。
为用户的使用提供便利。
下面仅就默认的Debug和Release版本的选项进行比较,详细的编译选项可以看MSDN 的说明。
我们将默认的Debug和Release的选项设置进行比较,过滤掉相同设置,主要的不同如下:编译选项:/Od /D "_DEBUG" /Gm /RTC1 /MDd /Fo"Debug““" /ZI链接选项:/OUT:"D:“MyProject“logging“Debug“OptionTest.dll" /INCREMENTAL Release设置:编译选项:/O2 /GL /D "NDEBUG" /FD /MD /Fo"Release““" /Zi链接选项:/OUT:"D:“MyProject“logging“Release“OptionTest.dll" /INCREMENTAL:NODebug 版本:/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 合并重复的字符串,并将字符串常量放到只读内存,防止被修改MDd与MD首先,Debug版本使用调试版本的运行时库(/MDd选项),Relase版本则使用的是发布版本的运行时库(vcrt.dll)。
VS中Deb ug模式和Relea se模式的区别有些人可能会问VC中的Debug和Rele ase模式到底有什么区别,能不能将两者混用。
这里是常见的看法:/en-US/vcgene ral/thread/775ce067-b225-4141-8b86-2d7e9b61db97/ syperk说:"Asares ult,I'veswit chedt ocomp iling mydeb ugbui ldsus ingth e/MDswit ch.Ifyoud othis,youals ohave tound efine the_D EBUGp repro cesso rmacr o.Otherw iseyo ugetl otsof error sabou t_Crt Debug Repor tW()notfou nd.Thisfu nctio nisap paren tlyin serte dinto yourc odeif_DEBU Gisde fined,butiso nlyde fined inthe debug CRT. Aslong asopt imiza tioni soffi tseem stobe perfe ctlyp ossib letod ebugp rogra mscom piled thisw ay,andthe reare nocon cerns about CRTin compa tibil ity.".但是他的说法并不完全正确。
混用有很多问题。
其中,最重要的一点是,混用通过不同的编译参数生成的模块是非常糟糕的做法,特别是涉及到条件编译的时候。
vc错误解决现象是用debug中的step info或者run to cursor合着设置断点会出现One or morebreakpoints cannot and have been disabled. Execution will stopat the beginning of the program.解决方法,1.在project->setting->c/c++-general->debug info->program databse for edit and continue ;2.edit->breakpoints->removeall;3.build-> active project configuration->*.exe win32 debug.经过以上三步,ok!另一篇:一、两种调试方式:1、Debug模式:2、Release模式:单击菜单[Project]->[Settings…],出现"Project Settings"对话框,选择“Release”状态;将C/C++标签中的Category选择General,Optimizations选Disable (Debug),Debug info选Program Database;在Link标签中选中Generate debug info复选框。
注:Release模式稍好点,因为某些情况程序只能在Release下运行,如“显示对话框的DLL”在Debug下显示出窗口后就死在那里。
二、调试中可能的错误:“不能调试”、“断点错误”。
1、“不能调试”:“One or more breakpoints cannot and have been disabled. Execution will stop at the beginning of the program.”2、“断点错误”:"One or more breakpoints are not positioned on valid lines.These breakpoints have been moved to the next valid line."注意:“不能调试”跟“断点错误”不是一回事,“断点错误”会禁用某些错误断点,但“不能调试”则会禁用所有断点。
[VC笔记]Debug和Release的困惑
Lightning[0GiNr]
PS:补充笔记,本文是在VC6的环境下编写的,VS2008无此BUG。
这些日子在写一些代码时遇到了一件令人非常囧的事情:同样的源码,DEBUG版本行为完全正常,RELEASE版本行为完全不正常。
按以往的经验,这一般都是#ifdef _DEBUG之类的宏导致了两个版本的行为不一致,但是我的程序中并没有显式或者隐式地使用过这个宏。
由于Release版本默认不支持调试,于是就把Release版本里挂上调试信息来试试。
在Project->Sittings的c/c++选项卡optimizations中选上Disable[DEBUG],Debug Info里填上Program Database,Link选项卡中搞定Generate debug info和Generate mapfile。
然后一按F5,行为完全和DEBUG版本一样正常!
可是一换回默认的release,问题就会出现,狂郁闷……
注释了几处代码,并且用printf输出了一些LOG后,我渐渐发现了问题所在:竟然是字符串操作的问题!
为了便于说明,我先写一个例子:
#include <windows.h>
#include <stdio.h>
int InsertString(char* pStr)
{
strcpy(pStr, "world");
return strlen(pStr);
}
int CreateString(char* pStr)
{
strcpy(pStr, "Hello ");
return strlen(pStr) + InsertString(pStr + strlen(pStr));
}
int main(void)
{
char szHello[64] = "";
int nLen = CreateString(szHello);
printf("%s LEN = %d\n", szHello, nLen);
system("pause");
return 0;
}
你能猜出程序的输出是什么吗?
DEBUG:
RELEASE:
数一下字符也知道,Release版本给出的LEN = 16是绝对错误的,问题出在CreateString函数的这一行:
return strlen(pStr) + InsertString(pStr + strlen(pStr));
在DEBUG版本或者加了调试信息的Release版中,VC编译器的编译结果是先计算加号左边的表达式,再计算右边的,这样,第一个strlen(pStr)计算的仅仅是"Hello "这个字符串的长度。
相应的汇编代码:
004010B9 8B 4D 08 mov ecx,dword ptr [ebp+8]
004010BC 51 push ecx
004010BD E8 DE 00 00 00 call strlen (004011a0) // 从左到右计算
004010C2 83 C4 04 add esp,4
004010C5 8B F0 mov esi,eax
004010C7 8B 55 08 mov edx,dword ptr [ebp+8]
004010CA 52 push edx
004010CB E8 D0 00 00 00 call strlen (004011a0)
004010D0 83 C4 04 add esp,4
004010D3 8B 4D 08 mov ecx,dword ptr [ebp+8]
004010D6 03 C8 add ecx,eax
004010D8 51 push ecx
004010D9 E8 2C FF FF FF call @ILT+5(InsertString) (0040100a)
004010DE 83 C4 04 add esp,4
004010E1 03 C6 add
但是在Release版本中就不一样了:
0040106C 8B FB mov edi,ebx
0040106E 83 C9 FF or ecx,0FFh
00401071 33 C0 xor eax,eax
00401073 F2 AE repne scas byte ptr [edi] // 这里是后面那个strlen
00401075 F7 D1 not ecx
00401077 49 dec ecx
00401078 03 CB add ecx,ebx // pStr + strlen(pStr);
0040107A 51 push ecx
0040107B E8 80 FF FF FF call 00401000 // 这里先调用了InsertString 函数
00401080 8B D0 mov edx,eax
00401082 8B FB mov edi,ebx
00401084 83 C9 FF or ecx,0FFh
00401087 33 C0 xor eax,eax
00401089 83 C4 04 add esp,4
0040108C F2 AE repne scas byte ptr [edi] // 然后把InsertString修改过的字符串进行strlen
0040108E F7 D1 not ecx
00401090 49 dec ecx
00401091 5F pop edi
00401092 03 D1 add edx,ecx
00401094 5E pop esi
00401095 8B C2 mov eax,edx
00401097 5B pop ebx
00401098 5D pop ebp
00401099 C3 ret
这样,最终的结果就是strlen("Hello world") + strlen("world") = 16了。
通过这个实例可以看出,类似于return strlen(pStr) + InsertString(pStr + strlen(pStr));之类的语句是应该在编程过程中尽力避免的,虽然一些C++手册上关于运算符结合顺序的论述会明确告诉你它会先计算加号前面的表达式(+的结合性是从左到右),但是并不是每一个非标准的编译器在每一个编译选项时都会这么做(有时出于优化的目的会调整顺序)。
而且这样做让别人看你的代码时也很容易迷惑不解。
解决方法:
int CreateString(char* pStr)
{
strcpy(pStr, "Hello ");
int nLen = strlen(pStr);
return nLen + InsertString(pStr + nLen );
}
不过,这里需要补充一句,上面的代码是在VC6下编译的,如果在VS2008下编译就不会出现这样的情况,结果都是LEN = 11。