VC调试技巧
1.没有解决的外部定义错误
XXView.obj : error LNK2001: unresolved external symbol "public: __thiscall CMyOCRInfo::CMyOCRInfo(void)" (??0CMyOCRInfo@@QAE@XZ)可能是由于构造方法没有实现代码
2.没有释放 HBITMAP 句柄造成不能创建位图的问题
没有及时的释放 HBITMAP 句柄,当创建位图数达到 30 时,出现 8 号错误,即内存不足3.在调试窗口输出信息
TRACE(s);4.如果用向导新建类时只有自定义类型,则可能是因为 .clw 文件没有生成,生成该文件即可
5.变量的类型范围
要注意变量的类型范围,强制转换可能会导致数据溢出;下面的示例将导致死循环:
for(byte i=0;i<256;i++){
... ...
}
注:由于 byte 类型的表示范围为:0-255 ,因此 i 永远不可能大于 255 ,所以导致死锁
6.没有包含 stdafx.h
没有包含 stdafx.h 导致的错误:
fatal error C1010: unexpected end of file while looking for divcompiled header directive
在 .cpp 文件中包含 stdafx.h 文件即可
7.避免重复包含头文件
#ifndef _INC_PUBLIC_H_32564987132178947#define _INC_PUBLIC_H_32564987132178947
// 中间写代码
...
#endif
注:#ifndef 可以改为 #if !defined
8.重复定义错误
出现重复定义错误,例如:
d:\program files\microsoft visual studio\vc98\include\dbdaoint.h(33) : error C2011: 'EditModeEnum' : 'enum' type redefinition
先检查所有的 .h 文件中是否定义了 include "stdafx.h" ,删之
再在所有的 .cpp 文件中搜索 stdafx ,看是否重复包含了 stdafx.h 文件,把重复的删掉
又如:
RefImageDll.obj : error LNK2005: "int __cdecl GetCameraRefImageIndexOfID1(class CArray<class CCameraRefImage,class CCameraRefImage> &,int)" (?GetCameraRefImageIndexOfID1@@YAHAAV?$CArray@VCCameraRefImage@@V1@@@H@Z) already defined in InterFaceFile.obj
是因为在 .h 文件中定义了一个函数,且做了完整的实现,因此被多个文件包含时就会出现重复定义错误,解决的方法是
把该函数定义成 inline ,这样就不再是一个函数而直接采用一段代码替换了,如:
int inline GetAge(){
return 10;
}
9.类定义不完整
例如:
refimagedll.h(14) : error C2236: unexpected 'class' 'CRefImageListDll'
一般是因为在该文件包含的文件中存在类定义不完整,例如:
calss a{
int age;
}
后面少了一个分号,应该改成:
class a{
int age;
};
10.纯虚类不能生成实例
例如:
d:\program files\microsoft visual studio\vc98\mfc\include\afxtempl.h(201) : error C2259: 'CKernel' : cannot instantiate abstract class due to following members:
如下:
class a{
public:
virtual void SetValue(int i)=NULL;
}
class b : public a{
private:
int m_iID;
public:
}
这样的话,B 也不能实例化,因为在 B 中没有实现 SetValue() 方法,在 B 中实现 SetValue() 方法即可解决。
正在装载数据…… |
11.重复释放导致的问题
User breakpoint called from code at 0x77f9193c以上原因是由于释放了一个类的成员,最后在作该类的析构时由于它的成员已经被释放导致出错(该成员被释放但是没有设 NULL)
12.试图执行系统不支持的操作
请检查当前窗口模块是否使用了其他不属于它自己的资源13.在 Dll 里调用对话框等资源的方法(如何在动态链接库中显示对话框)
在动态链接库的显示对话框函数中加入下面这句代码即可:AFX_MANAGE_STATE(AfxGetStaticModuleState());
如果需要导出对话框的对象,在外面进行显示,则可以重载 DoModal() 方法,在该方法中加入 AFX_MANAGE_STATE(AfxGetStaticModuleState());
14.动态链接库和静态链接库混用的问题
症状:使用 LoadLibrary() 加载一个动态链接库时,返回 0 ,函数不成功,调用 GetLastError() 返回结果 126 ,MSDN 如是说:126 The specified module could not be found. ERROR_MOD_NOT_FOUND
经研究发现,是因为该动态链接库采用了静态的方式调用了另一个链接库(B),而另一个链接库则又采用静态的方式调用了另一个动态链接库(C),但是 C 却没有拷贝到程序所在目录,所以导致不能正常加载
15.宏定义导致系统出错
连出三个错误:ignored on left of 'unsigned char' when no variable is declared
error C2143: syntax error : missing ';' before 'constant'
error C2106: '=' : left operand must be l-value
代码如下:
byte R=(byte)(AColor & 0xFF);
如果按照常理,应该不会有问题,但是由于一个函数库里面对 R 有定义,所以 R 便不能当做变量使用
16.Debug 版本的 GetDocument() 函数可用,而 Release 版本则不能使用,提示函数没有实现代码
检查 .h 文件中最后是否函数如下代码:#ifndef _DEBUG // debug version in LCDModelView.cpp
inline CXXXDoc* CXXXView::GetDocument()
{ return (CXXXDoc*)m_pDocument; }
#endif
原理:对于从来都没有调用的函数,可以没有其实现代码,如果含有有一处调用则一定要有其实现代码,微软采用宏定义来区分 Release 和 Debug 版本。
17.VC 环境下不能使用 FindFile 进行文件搜索的问题
在 VC 的集成开发环境下,点击搜索文件按钮,VC 崩溃检查注册表键:HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\SearchOld\FIF_InFolders
发现其值为一个不存在的目录,导致崩溃,解决办法是清除该键值即可
调试工具:
VC中调试MFC程序没有方便的printf可用,为了找出程序错误的位置可以使用以下方法:
1. 在可能出错的地方加代码,将输出写入到log文件。
2. 使用TRACE宏,例如:
TRACE("Hello World!");
这样便会输出到调试窗口而不影响程序运行。这种方法只在debug模式下有用,并且只能输出字符串。
3. 输出到afxDump去:
#ifdef _DEBUG
afxDump<<"hello world"<<i<<endl; // i 是整数变量
#endif
一、TRACE宏
当选择了Debug目标,并且afxTraceEnabled变量被置为TRUE时,TRACE宏也就随之被激活了。但在程序的Release版本中,它们是被完全禁止的。下面是一个典型的TRACE语句:
int nCount =9;
Cstring strDesc("total");
TRACE("Count =%d,Description =%s\n",nCount,strDesc);
可以看到,TRACE语句的工作方式有点像C语言中的printf语句,TRACE宏参数的个数是可变的,因此使用起来非常容易。如果查看MFC的源代码,你根本找不到TRACE宏,而只能看到TRACE0、TRACE1、TRACE2和TRACE3宏,它们的参数分别为0、1、2、3。
二、ASSERT宏
如果你设计了一个函数,该函数需要一个指向文档对象的指针做参数,但是你却错误地用一个视图指针调用了这个函数。这个假的地址将导致视数据的破坏。现在,这种类型的问题可以被完全避免,只要在该函数的开始处实现一个assert测试,用来检测该指针是否真正指向一个文档对象。一般来讲,编程者在每个函数的开始处均应例行公事地使用assertion。assert宏将会判断表达式,如果一个表达式为真,执行将继续,否则,程序将显示一条消息并且暂停,你可以选择忽视这条错误并继续、终止这个程序或者是跳到Debug器中。下面一例演示了如何使用一个ASSERT宏去验证一个语句。
void foo(char p,int size)
{ASSERT(p != 0); //确认缓冲区的指针是有效的
ASSERT((size 〉= 100); //确认缓冲区至少有100个字节
// Do the foo calculation
}
这些语句不产生任何代码,除非—DEBUG处理器标志被设置。Visual C++只在Debug版本设置这些标志,而在Release版本不定义这些标志。当—DEBUG被定义时,两个assertions将产生如下代码:
//ASSERT(p != 0);
do{if(!(p != 0) &&
AfxAssertFailedLine(—FILE—,—LINE—))AfxDebugBreak();
} while(0); //ASSERT((size 〉= 100);
do{if(!(size 〉= 100) &&
AfxAssertFailedLine(—FILE—,—LINE—))AfxDebugBreak();
} while(0);
D0-while循环将整个assertion封装在一个单独的程序块中,使得编译器编译起来很舒畅。If语句将求取表达式的值并且当结果为零时调用AfxAssertFailedLine()函数。这个函数将弹出一个对话框,其中提供三个选项“取消、重试或忽略”,当你选取“重试”时,它将返回TRUE。重试将导致对AfxDebugBreak()函数的调用,从而激活调试器。
AfxAssertFailedLine()是一个未正式公布的函数,它的功能就是显示一个消息框。该函数的源代码驻留在afxasert.cpp中。函数中的—FILE—和—LINE—语句是处理器标志,它们分别指定了源文件名和当前的行号。
三、VERIFY 宏
因为assertion只能在程序的Debug版本中起作用,在表达式中不可以包含赋值语句、增加语句(++)或者是减少语句(--),因为,这些语句实际改变数据。可有时你可能想要验证一个能动的表达式,使用一个赋值语句。那么就到了用VERIFY宏来替代ASSERT。例如:
void foo(char p,int size)
{char q;
VERIFY(q = p);
ASSERT((size 〉= 100);
// Do the foo calculation}
在Debug模式下,ASSERT和VERIFY是一回事,但是在Release模式下,VERIFY宏仍然测试表达式而assertion却不起任何作用。可以说,在Release模式下,ASSERT语句被删除了。
请注意,如果你在一个ASSERT语句中错误地使用了一个能动的表达式,编译器将不做任何警告地忽略它。在Release模式下,该表达式就会被无声息地删除掉,这将会导致程序的错误运行。由于Release版的程序通常不包含Debug信息,这类错误将很难被发现。