黎一墨背景:VC++一些常用调试方法

来源:百度文库 编辑:九乡新闻网 时间:2024/04/26 21:35:26

转载自http://anony3721.blog.163.com/blog/static/511974201142151133369/Watch
VC支持查看变量、表达式和内存的值。所有这些观察都必须是在断点中断的情况下进行。
观看变量的值最简单,当断点到达时,把光标移动到这个变量上,停留一会就可以看到变量的值。
VC提供一种被成为Watch的机制来观看变量和表达式的值。在断点状态下,在变量上单击右键,选择Quick Watch,就弹出一个对话框,显示这个变量的值。
单击Debug工具条上的Watch按钮,就出现一个Watch视图(Watch1,Watch2,Watch3,Watch4),在该视图中输入变量或者表达式,就可以观察 变量或者表达式的值。注意:这个表达式不能有副作用,例如++运算符绝对禁止用于这个表达式中,因为这个运算符将修改变量的值,导致 软件的逻辑被破坏。
Memory
由于指针指向的数组,Watch只能显示第一个元素的值。为了显示数组的后续内容,或者要显示一片内存的内容,可以使用memory功能。在 Debug工具条上点memory按钮,就弹出一个对话框,在其中输入地址,就可以显示该地址指向的内存的内容。
Varibles

Debug工具条上的Varibles按钮弹出一个框,显示所有当前执行上下文中可见的变量的值。特别是当前指令涉及的变量,以红色显示。
寄存器
Debug工具条上的Reigsters按钮弹出一个框,显示当前的所有寄存器的值。Call Stack

调用堆栈反映了当前断点处函数是被那些函数按照什么顺序调用的。单击Debug工具条上的Call stack就显示Call Stack对话框。在CallStack对话框中显示了一个调用系列,最上面的是当前函数,往下依次是调用函数的上级函数。单击这些函数名可以跳到对应的函数中去。

进程控制
VC允许被中断的程序继续运行、单步运行和运行到指定光标处,分别对应快捷键F5、F10/F11和CTRL+F10。各个快捷键功能如下: 
 

快捷键

说明

F5

调试/继续运行

F10

单步,如果涉及到子函数,不进入子函数内部

F11

单步,如果涉及到子函数,进入子函数内部

CTRL+F10

运行到当前光标处。

F7

重建

F9

设置断点/清除断点

Ctrl+Shift+F9

清除所有断点

Shift+F5

结束调试

  一 TRACE宏的使用

TRACE宏对于VC下程序调试来说是很有用的东西,有着类似printf的功能;该宏仅仅在程序的DEBUG版本中出现,当RELEASE的时候该宏就完全消失了,从而帮助你调试也在RELEASE的时候减少代码量。

VC中按下F5键GO,变量的值就会打印到DEBUG窗口,在output Debug窗口中可以看到用TRACE打印的信息。

TRACE宏使用格式如下:

TRACE("DDDDDDDDDDD");

TRACE("%d",333);

同样还存在TRACE0,TRACE1,TRACE2。。。分别对应0,1,2。。个参数

和Printf 函数一样,TRACE函数可以接受多个参数如:(将下面代码拷贝到单文档View类的OnDraw函数中,按F5)

int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );

二 断点的使用

位置断点(Location Breakpoint 
  大家最常用的断点是普通的位置断点,在源程序的某一行按F9就设置了一个位置断点。但对于很多问题,这种朴素的断点作用有限。譬如下面这段代码:

void CForDebugDlg::OnOK()

{

       int s = 0;

       for (int i = 0; i < 2000; i++)    //A

       {

              int k = i * 10 - 2; //B

               s++;          //C

              s--;  //D

              s++;    //E

       }

}       

  执行此函数,程序崩溃于E行,发现此时tmp为0,假设tmp本不应该为0,怎么这个时候为0呢?所以最好能够跟踪此次循环时DoSome函数是如何运行的,但由于是在循环体内,如果在E行设置断点,可能需要按F5(GO)许多次。这样手要不停的按,很痛苦。使用VC6断点修饰条件就可以轻易解决此问题。步骤如下。

1 Ctrl+B打开断点设置框,如下图: 

 Figure 1设置高级位置断点

  2 然后选择D行所在的断点,然后点击condition按钮,在弹出对话框的最下面一个编辑框中输入一个很大数目,具体视应用而定,这里1000就够了。 
  3 按F5重新运行程序,程序中断。Ctrl+B打开断点框,发现此断点后跟随一串说明:...487 times remaining。意思是还剩下487次没有执行,那就是说执行到513(1000-487)次时候出错的。因此,我们按步骤2所讲,更改此断点的skip次数,将1000改为513。 
  4 再次重新运行程序,程序执行了513次循环,然后自动停在断点处。这时,我们就可以仔细查看DoSome是如何返回0的。这样,你就避免了手指的痛苦,节省了时间。 
  再看位置断点其他修饰条件。如Figure 1所示,在“Enter the expression to be evaluated:”下面,可以输入一些条件,当这些条件满足时,断点才启动。譬如,刚才的程序,我们需要i为100时程序停下来,我们就可以输入在编辑框中输入“i==100”。 
  另外,如果在此编辑框中如果只输入变量名称,则变量发生改变时,断点才会启动。这对检测一个变量何时被修改很方便,特别对一些大程序。 
  用好位置断点的修饰条件,可以大大方便解决某些问题。

MFC中对象的DUMP函数的利用
    Dump 函数用来按指定的格式输出一个对象的成员变量,来帮助你诊断一个对象的内部情况。与AssertValid成员函数一样,Dump也是Cobject 类的成员函数。Dump函数的参数是一个CdumpContext对象,你可以象利用流一样往向这个对象中输入数据。当你创建一个Cobject继承而来的 新类时,你可以按如下步骤重载你自己的Dump函数:
(1) 调用基类的Dump函数,以输出基类的内容;
(2) 向Cdumpcontest对象输出该类的数据.
例如,典型的Dump函数定义如下:
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CObject::Dump( dc );

    // now do the stuff for our specific class
    dc << "last name: " << m_lastName << "\n"
        << "first name: " << m_firstName << "\n";
}
#endif
你可能已经注意到整个函数的定义都包含在#ifdef _DEBUG 和#endif中,这
使得Dump成员函数只在DEBUG版本中发生作用,而对RELEASE版本不发生作用。

Remarks: 在VC6.0中如何设置,使工程由Debug变为Release --- 找了半天才找见啊

在工具栏上或者菜单栏上点击右键,选中build选项(不是minibuild),然后在该工具栏上有一个选择编译版本的下拉框,可以选择“win32 Release”或者“win32 Debug”。