vc常用的调试命令,vc++调试程序

  vc常用的调试命令,vc++调试程序

  F10:调试到下一句,这里是单步跟踪。

  F11:调试到下一句,在函数内部跟进。

  Shift F11:跳出当前功能

  Ctrl F10:调试到光标位置

  F9:设置(取消)断点

  Alt F9:高级断点设置

  跟踪调试

  1.使用快捷键时尽量调试。

  2.观察调试信息。

  3.高级中断设置

  异常调试

  重试-取消-调试

  函数堆栈,使用变量或调用堆栈窗口

  发布调试

  1.始终测试您的调试和发布版本。

  2.不要删除调试代码,比如使用ASSERT、TRACE等。

  3.初始化变量,尤其是全局变量,内存和新内存。

  4.删除资源时,请确保删除了与该资源相关的所有语句(主要在resource . h文本中)。

  5.用警告级别3或4编译你的代码,确保没有警告,项目-设置-C/C-警告级别(中文版是项目-属性-C/C-常规-警告级别)

  6.将_debug改为NDEBUG进行调试,项目-设置-C/C-预处理器定义(中文版是项目-属性-C/C-预处理器-预处理定义)(这里是调试和发布编译的重要区别之一)

  7.调试发布中的源代码。项目-设置-C/C-调试信息选择程序数据库(中文版为项目-属性-C/C-通用-调试信息格式-程序数据库为“编辑并继续”),项目-设置-链接选择生成调试信息(中文版为项目-属性-链接器-调试-生成调试信息)

  8.浏览代码,特别注意堆栈和指针。

  第二,跟踪宏

  当选择了调试目标并且afxTraceEnabled变量设置为TRUE时,跟踪宏被激活。但是在该程序的发布版本中,它们被完全禁止。以下是典型的跟踪语句:

  …

  int nCount=9;

  CString strDesc( total );

  TRACE(Count=%d,Description=%sn ,nCount,strDesc);

  …

  如您所见,TRACE语句的工作方式有点像C语言中的printf语句。跟踪宏参数的数量是可变的,因此非常容易使用。如果看MFC的源代码,根本找不到TRACE宏,只能看到TRACE0,TRACE1,TRACE2,TRACE3宏,它们的参数分别是0,1,2,3。

  个人总结:最近看网络编程的时候碰到了TRACE语句。不知道从哪里输出,但是一晚上都没找到。今天,我终于找到了。该方法如下:

  1.在MFC中添加跟踪语句。

  2.在工具- MFCTRACER中选择“启用跟踪”,然后单击确定

  3.进行调试操作,GO(F5)(特别说明:不是执行!之前看不到跟踪内容的原因是不是调试执行,而是!记得,记得,记得)

  4.然后,您将在输出的调试窗口中看到跟踪内容,调试执行将自动从构建窗口跳转到调试窗口,您将在那里看到跟踪内容。_

  以下是您要寻找的踪迹的详细介绍:

  ==============================

  TRACE对于VC下的程序调试来说是一个非常有用的东西,有类似printf的功能;这个宏只出现在程序的调试版本中,发布时完全消失,从而帮助你在发布时调整和减少代码量。

  使用起来非常简单,格式如下:

  TRACE( dddddddddd );

  TRACE(wewe%d ,333);

  TRACE0、TRACE1和TRACE2也存在。分别对应于0、1和2。因素

  跟踪信息输出到VC IDE环境的“输出”窗口(该窗口是编译项目错误提示的窗口),但前提是在VC中运行程序的调试版本。

  还可以使用DEBUGVIEW捕获跟踪信息。在这种情况下,你不能在VC的IDE环境下运行你的程序,只能单独运行程序的BUILD DEBUG版本。此时,您可以在DEBUGVIEW窗口中看到DEBUGVIE格式的输出。

  VC跟踪在中有四种用法:

  1,即没有动态参数的输出字符串,类似于C的printf(“输出字符串”);

  TRACE2: 2中的字符串可以用一个参数输出,类似于printf(.%d ,变量);

  3: TRACE3可以用两个参数输出,就像C(的printf.% d.% f ,变量1,变量2);

  TRACE4可以用三个参数输出,比如C(的printf.%d,%d,% d ,变量1,变量2,变量3);

  TRACE有点像我们之前在C语言中使用的Printf函数,让程序在运行过程中输出一些调试信息,让我们知道程序的一些状态。但是有一点不同:

  TRACE只能在调试时输出,而之前使用的Printf函数在任何情况下都可以输出。像Printf函数一样,TRACE函数可以接受多个参数,例如:

  int x=1;

  int y=16

  float z=32.0

  TRACE(这是一个跟踪语句 n );

  TRACE( x的值是%dn ,x);

  TRACE( x=%d和y=%dn ,x,y);

  TRACE( x=%d和y=%x和z=%fn ,x,y,z);

  请注意,跟踪宏仅适用于项目的调试版本,在项目的发布版本中,跟踪宏将被忽略。

  三。断言宏

  如果您设计了一个需要指向文档对象的指针作为参数的函数,但是您错误地用视图指针调用了这个函数。这个假地址会导致视频数据的破坏。现在,这种类型的问题可以完全避免,只要在函数的开头实现一个ASSERT测试来检测指针是否真的指向一个文档对象。一般来说,程序员通常应该在每个函数的开头使用断言。ASSERT宏将判断表达式。如果表达式为真,执行将继续。否则,程序将显示一条消息并暂停。您可以选择忽略此错误并继续,终止程序或跳转到调试。下面的示例演示如何使用ASSERT宏来验证语句。

  void foo(char p,int size)

  {

  断言(p!=0);//确认指向缓冲区的指针有效

  ASSERT((size=100);//确认缓冲区至少有100个字节

  //进行foo计算

  }

  除非设置了DEBUG处理器标志,否则这些语句不会生成任何代码。visual ++只在调试版本中设置这些标志,在发布版本中没有定义。当定义了—DEBUG时,两个断言将生成以下代码:

  //ASSERT( p!=0 );

  做{

  如果(!(p!=0)AfxAssertFailedLine(—文件—,—行—))

  AfxDebugBreak();

  } while(0);

  //ASSERT((size100);

  做{

  如果(!(size=100)& & AfxAssertFailedLine(—文件—,—行—))

  AfxDebugBreak();

  } while(0);

  Do-while循环将整个断言封装在一个块中,这使得编译器很容易编译。If语句计算表达式,并在结果为零时调用AfxAssertFailedLine()函数。这个函数会弹出一个对话框,有“取消、重试、忽略”三个选项,选择“重试”会返回TRUE。重试将导致调用AfxDebugBreak()函数,从而激活调试器。

  AfxAssertFailedLine()是一个未公开的函数,它的作用是显示一个消息框。此函数的源代码驻留在afxasert.cpp中。函数中的-file-and-line-语句是处理器标志,分别指定源文件名和当前行号。

  AfxAssertFailedLine()是一个未公开的函数,它的作用是显示一个消息框。此函数的源代码驻留在afxasert.cpp中。函数中的-file-and-line-语句是处理器标志,分别指定源文件名和当前行号。

  第四,验证宏观

  因为断言只能在程序的调试版本中起作用,所以不能在表达式中包含赋值语句、增加语句(++)或减少语句(-),因为这些语句实际上改变了数据。但有时你可能想验证一个动态表达式,并使用赋值语句。那么是时候用VERIFY宏替换ASSERT了。例如:

  voidfoo(char p,int size)

  {

  char q;

  验证(q=p);

  ASSERT((size100);

  //进行foo计算

  //进行foo计算

  }

  在调试模式下,断言和验证是一回事,但是在发布模式下,验证宏仍然测试表达式,断言没有任何作用。可以说在发布模式下,ASSERT语句已经被删除了。

  请注意,如果您在ASSERT语句中错误地使用了活动表达式,编译器将会忽略它而不会发出警告。在Release模式下,表达式会被静默删除,导致程序的错误操作。因为程序的发布版本通常不包含调试信息,这样的错误将很难被发现。

  动词(verb的缩写)VC高级调试方法-设置条件和数据断点

  (a)位置断点(LocationBreakpoint)

  最常用的断点是常用位置断点,在源程序的一行按F9设置一个位置断点。但是对于很多问题来说,这个简单的断点作用有限。例如,下面的代码:

  void CForDebugDlg:OnOK()

  {

  for(int I=0;i 1000i ) //A

  {

  intk=I * 10-2;//B

  send to(k);//C

  int tmp=do some(I);//D

  Trace0(这里输出什么);//这里可以输出一些有用的信息,也可以输出I的值,两者都可以。

  intj=I/tmp;//E

  }

  }

  //其实我们也可以用别的方法,模式也是一样的。可以使用TRACE0宏输出循环中的每一个结果,我们也可以在debug中看到输出结果。出错时,输出结果可能会有所不同。我们可以在调试中分析结果,找出问题所在。

  当执行这个函数时,程序在E行崩溃,此时发现tmp为0。假设tmp不应该是0,这个时候怎么会是0呢?所以最好跟踪DoSome函数在这个循环中是如何运行的,但是由于它在循环内部,如果在E行设置断点,可能需要多次按F5(GO)。一直这样按手很痛苦。通过用VC6断点修改条件,可以很容易地解决这个问题。步骤如下。

  Ctrl B打开断点设置框,如下图所示:

  图1设置了高级位置断点

  2然后选择D行所在的断点,然后点击条件按钮,在弹出的对话框底部的编辑框中输入一个大数。视应用而定,这里1000就够了。

  按F5重新运行程序,程序中断。B Ctrl打开断点框,找到这个断点后,后面跟着一串指令:还剩487次。意味着还剩下487次要执行,也就是说执行到513 (1000-487)次就有错误了。因此,我们将这个断点的跳转号从1000更改为513,如步骤2中所述。

  4再次运行程序,程序执行513个循环,然后自动停在断点处。此时,我们可以仔细看看DoSome是如何返回0的。这样可以避免手指的疼痛,节省时间。

  再看位置断点的其他修改条件。如图1所示,在“输入要计算的表达式:”下,可以输入一些条件。当满足这些条件时,断点将开始。比如刚才的程序,我们需要程序在I为100时停止,那么我们可以在编辑框中输入“i==100”。

  此外,如果仅在此编辑框中输入变量名,则断点将仅在变量更改时启动。这便于检测变量何时被修改,尤其是对于一些大型程序。

  利用好位置断点的修改条件,可以大大解决一些问题。

  (2)数据断点(数据断点)

  在软件调试的过程中,有时候会发现有些数据会被莫名其妙的修改(比如有些数组写越界了,导致覆盖了其他变量)。找出代码导致内存改变的地方是一件棘手的事情(没有调试器的帮助)。正确使用数据断点可以快速帮助您定位数据被修改的时间和位置。例如,下面的程序:

  #include stdafx.h

  #包含字符串. h

  int main(int argc,char* argv[])

  {

  charszname 1[10];

  charszname 2[4];

  strcpy(szName1,深圳);

  printf(%sn ,SZ name 1);//A

  strcpy(szName2, vck base );//B

  printf(%sn ,SZ name 1);

  printf(%sn ,SZ name 2);

  return0

  }

  这个程序的输出是

  深圳名称1:深圳

  szName1:ase

  szName2:vckbase

  首先我给大家分析一下为什么会是这样的结果!首先你在strcpy(szName1,深圳);这个地方F9设置了一个断点,然后F5运行程序,也就是说程序会中断到我们设置的断点,如下图所示。

  看,这就是问题的原因。系统分配给szName2的地址是0x0012ff70,这里是4个字节。然后,在0x0012ff70之后的4个字节,分配szName1的10个字节,即在0x0012ff74,

  F10单步跟踪,来行printf(%sn ,szName1),如下图所示。

  szName1分配的空间已经附加了一个值。

  F10转到下一个printf(%sn ,szName1),并查看下图。

  因为szName1和szName2分配的空间是连续的,所以当赋给szName2的值超过它所包含的字节数时,szName1的内容就开始被覆盖,所以当我们输出结果时,就出现了意想不到的结果。

  那么如何调试呢?下面是具体的方法。

  szName1是什么时候修改的?因为szName1代码没有明显的修改。我们可以先在A行设置一个公共断点,F5运行程序,程序停在A行,然后我们设置一个数据断点。如下图:

  图2数据断点

  F5继续运行,程序停在B行,表示B处的代码修改了szName1。为什么没有在B位修改szName1?但是调试器指示这一行,一般是正确的,还是静下心来看看程序比较好。哦,你发现szName2只有4个字节,而strcpy有7个字节,所以覆盖了szName1。

  数据断点不仅对变量变化有效,还可以设置变量是否等于某个值。例如,您可以将图2中的红色圆圈更改为条件“szName2 [0]== y ”,那么断点将在szName2的第一个字符为y时开始。

  可以看出,数据断点和断点的一个很大的区别是,你不用指定在哪一行代码上设置断点。

  (3)其他

  1在调用堆栈窗口设置断点,选择一个函数,按F9设置断点。这样就可以从深层的函数调用中快速返回到需要的函数。

  2设置下一条语句命令(该命令在调试期间右击debug)

  该命令的功能是将程序的指令指针(EIP)指向不同的代码行。比如你正在调试上面的代码,它运行在A行,但是你不想运行B行和c行,这时候你可以右击D行,然后“设置下一条语句”。调试器不会执行B行和c行,只要是在同一个函数中,这个指令可以在跳转之前或之后任意执行。灵活使用该功能可以节省大量的调试时间。

  3观察窗

  监视窗口支持丰富的数据格式化功能。如果输入0x65,u,右栏将显示101。

  显示实时windows API调用的错误:在左栏输入@err,hr。

  调用"监视"窗口中的函数。作为提醒,在调用该函数后,请立即清除“监视”窗口中的该函数,否则,调试器将在单步调试期间的每一步都调用该函数。

  4消息断点不是很实用。基本上可以用前面提到的断点来代替。

  六个。VC调试环境设置

  为了调试程序,必须首先在程序中包含调试信息。通常从AppWizard创建的项目中包含的调试配置会自动包含调试信息,但是否是调试版本并不是程序包含调试信息的决定性因素。程序员可以在任何配置中添加调试信息,包括发布版本。

  为了添加调试信息,您可以遵循以下步骤:

  打开“项目设置”对话框(可通过快捷键ALT F7或IDE菜单“项目/设置”打开)

  选择C/C页,在类别中选择常规,将出现调试信息下拉列表框。调试信息选项包括:

  命令行项目设置描述

  无无没有调试信息

  /Zd Line Numbers Only目标文件或可执行文件仅包含全局和导出的符号和代码行信息,但不包含符号调试信息。

  /Z7 C7.0兼容的目标文件或可执行文件包含所有符号的行号和调试信息,包括变量名和类型、函数和原型等。

  /Zi程序数据库创建一个程序库(PDB),包括类型信息和符号调试信息。

  /ZI程序数据库

  除了前面的/Zi,Edit and Continue功能之外,该选项还允许在调试期间修改和继续执行代码。此选项还会使#pragma设置的优化函数无效。

  选择链接页面并选中复选框“Generate DebugInfo”,这将使连接器将调试信息写入可执行文件和DLL。

  如果在C/C页面中设置了程序数据库上方的选项,则可以增量选择链接。选择此选项将使程序能够在上次编译的基础上进行编译(即增量编译),而不必每次都从头编译。

  郭于是乎又做收集和整理。

vc常用的调试命令,vc++调试程序