VC调试技巧

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信息,这类错误将很难被发现。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法哥2012

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值