VC调试

VC调试入门


作者:阿荣


概述
调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使会一门语言,却不能编制出任何好的软件。
这里我简要的根据自己的经验列出调试中比较常用的技巧,希望对大家有用。
本文约定,在选择菜单时,通过/表示分级菜单,例如File/Open表示顶级菜单File的子菜单Open。
 
设置
为了调试一个程序,首先必须使程序中包含调试信息。一般情况下,一个从AppWizard创建的工程中包含的Debug Configuration自动包含调试信息,但是是不是Debug版本并不是程序包含调试信息的决定因素,程序设计者可以在任意的Configuration中增加调试信息,包括Release版本。
为了增加调试信息,可以按照下述步骤进行:

  • 打开Project settings对话框(可以通过快捷键ALT+F7打开,也可以通过IDE菜单Project/Settings打开)
  • 选择C/C++页,Category中选择general ,则出现一个Debug Info下拉列表框,可供选择的调试信息 方式包括:
     
    命令行Project settings说明
    None没有调试信息
    /ZdLine Numbers Only目标文件或者可执行文件中只包含全局和导出符号以及代码行信息,不包含符号调试信息
    /Z7C 7.0- Compatible目标文件或者可执行文件中包含行号和所有符号调试信息,包括变量名及类型,函数及原型等
    /ZiProgram Database创建一个程序库(PDB),包括类型信息和符号调试信息。
    /ZIProgram Database for Edit and Continue除了前面/Zi的功能外,这个选项允许对代码进行调试过程中的修改和继续执行。这个选项同时使#pragma设置的优化功能无效

  • 选择Link页,选中复选框"Generate Debug Info",这个选项将使连接器把调试信息写进可执行文件和DLL
  • 如果C/C++页中设置了Program Database以上的选项,则Link incrementally可以选择。选中这个选项,将使程序可以在上一次编译的基础上被编译(即增量编译),而不必每次都从头开始编译。
断点
断点是调试器设置的一个代码位置。当程序运行到断点时,程序中断执行,回到调试器。断点是 最常用的技巧。调试时,只有设置了断点并使程序回到调试器,才能对程序进行在线调试。

设置断点:可以通过下述方法设置一个断点。首先把光标移动到需要设置断点的代码行上,然后
  • 按F9快捷键
  • 弹出Breakpoints对话框,方法是按快捷键CTRL+B或ALT+F9,或者通过菜单Edit/Breakpoints打开。打开后点击Break at编辑框的右侧的箭头,选择 合适的位置信息。一般情况下,直接选择line xxx就足够了,如果想设置不是当前位置的断点,可以选择Advanced,然后填写函数、行号和可执行文件信息。
去掉断点:把光标移动到给定断点所在的行,再次按F9就可以取消断点。同前面所述,打开Breakpoints对话框后,也可以按照界面提示去掉断点。

条件断点:可以为断点设置一个条件,这样的断点称为条件断点。对于新加的断点,可以单击Conditions按钮,为断点设置一个表达式。当这个表达式发生改变时,程序就 被中断。底下设置包括“观察数组或者结构的元素个数”,似乎可以设置一个指针所指向的内存区的大小,但是我设置一个比较的值但是改动 范围之外的内存区似乎也导致断点起效。最后一个设置可以让程序先执行多少次然后才到达断点。

数据断点:数据断点只能在Breakpoints对话框中设置。选择“Data”页,就显示了设置数据断点的对话框。在编辑框中输入一个表达式,当这个 表达式的值发生变化时,数据断点就到达。一般情况下,这个表达式应该由运算符和全局变量构成,例如:在编辑框中输入 g_bFlag这个全局变量的名字,那么当程序中有g_bFlag= !g_bFlag时,程序就将停在这个语句处。

消息断点:VC也支持对Windows消息进行截获。他有两种方式进行截获:窗口消息处理函数和特定消息中断。
在Breakpoints对话框中选择Messages页,就可以设置消息断点。如果在上面那个对话框中写入消息处理函数的名字,那么 每次消息被这个函数处理,断点就到达(我觉得如果采用普通断点在这个函数中截获,效果应该一样)。如果在底下的下拉 列表框选择一个消息,则每次这种消息到达,程序就中断。


Watch
VC支持查看变量、表达式和内存的值。所有这些观察都必须是在断点中断的情况下进行。
观看变量的值最简单,当断点到达时,把光标移动到这个变量上,停留一会就可以看到变量的值。
VC提供一种被成为Watch的机制来观看变量和表达式的值。在断点状态下,在变量上单击右键,选择Quick Watch, 就弹出一个对话框,显示这个变量的值。
单击Debug工具条上的Watch按钮,就出现一个Watch视图(Watch1,Watch2,Watch3,Watch4),在该视图中输入变量或者表达式,就可以观察 变量或者表达式的值。注意:这个表达式不能有副作用,例如++运算符绝对禁止用于这个表达式中,因为这个运算符将修改变量的值,导致 软件的逻辑被破坏。

Memory
由于指针指向的数组,Watch只能显示第一个元素的值。为了显示数组的后续内容,或者要显示一片内存的内容,可以使用memory功能。在 Debug工具条上点memory按钮,就弹出一个对话框,在其中输入地址,就可以显示该地址指向的内存的内容。

Varibles

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

寄存器
Debug工具条上的Reigsters按钮弹出一个框,显示当前的所有寄存器的值。

进程控制
VC允许被中断的程序继续运行、单步运行和运行到指定光标处,分别对应快捷键F5、F10/F11和CTRL+F10。各个快捷键功能如下:
 
快捷键说明
F5继续运行
F10单步,如果涉及到子函数,不进入子函数内部
F11单步,如果涉及到子函数,进入子函数内部
CTRL+F10运行到当前光标处。

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

其他调试手段
系统提供一系列特殊的函数或者宏来处理Debug版本相关的信息,如下:

宏名/函数名说明
TRACE使用方法和printf完全一致,他在output框中输出调试信息
ASSERT它接收一个表达式,如果这个表达式为TRUE,则无动作,否则中断当前程序执行。对于系统中出现这个宏 导致的中断,应该认为你的函数调用未能满足系统的调用此函数的前提条件。例如,对于一个还没有创建的窗口调用SetWindowText等。
VERIFY和ASSERT功能类似,所不同的是,在Release版本中,ASSERT不计算输入的表达式的值,而VERIFY计算表达式的值。

关注
一个好的程序员不应该把所有的判断交给编译器和调试器,应该在程序中自己加以程序保护和错误定位,具体措施包括:

  • 对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用绝对不会出错,或者不关心它是否出错。
  • 一些函数返回错误,需要用其他函数获得错误的具体信息。例如accept返回INVALID_SOCKET表示accept失败,为了查明 具体的失败原因,应该立刻用WSAGetLastError获得错误码,并针对性的解决问题。
  • 有些函数通过异常机制抛出错误,应该用TRY-CATCH语句来检查错误
  • 程序员对于能处理的错误,应该自己在底层处理,对于不能处理的,应该报告给用户让他们决定怎么处理。如果程序出了异常, 却不对返回值和其他机制返回的错误信息进行判断,只能是加大了找错误的难度。
另外:VC中要编制程序不应该一开始就写cpp/h文件,而应该首先创建一个合适的工程。因为只有这样,VC才能选择合适的编译、连接 选项。对于加入到工程中的cpp文件,应该检查是否在第一行显式的包含stdafx.h头文件,这是Microsoft Visual Studio为了加快编译 速度而设置的预编译头文件。在这个#include "stdafx.h"行前面的所有代码将被忽略,所以其他头文件应该在这一行后面被包含。
对于.c文件,由于不能包含stdafx.h,因此可以通过Project settings把它的预编译头设置为“不使用”,方法是:
  • 弹出Project settings对话框
  • 选择C/C++
  • Category选择Precompilation Header
  • 选择不使用预编译头。
 

Visual Studio 2005中编译调试新功能

作者:廖煜嵘编译 来源:天极开发

在日常的编程中,程序员经常离不开的工作之一,就是调试。当写好一段代码或程序后,在运行后总会出现这样那样的问题,比如各样的错误,程序员就必须进行调试,将这些错误排除。在最新出炉的Visual Studio 2005中,将提供十分强大而方便的调试功能,从而使程序员能节约时间,提高工作效率。本文中,将主要介绍Visual Studio 2005 在调试方面新增加的几个重要功能,并介绍它们的简单用法。
  
   Edit and Contiue功能

  在调试程序的时候,经常会遇到这样的情况:在调试一大段代码时,遇到了一个小的错误,比如参数的赋值错误了,这时候,往往希望能够马上将这些小的错误改正过来后,能够继续调试跟踪下去,而不用结束整个调试过程去修改。在Visual Studio 2003中,我们必须停止当前的调试,修改错误的地方,再重新编译,这样十分不方便。在Visual Studio 2005 中,提供了一个新的功能叫"edit and continue",意思是说,当你在调试时,遇到小的错误需要马上修改后,可以进行编辑修改,然后继续往下调试,不需要结束整个调试的过程,当你修改后,调试器在后台进行了自动的编译,并且会执行新修改的代码,十分方便。下面举个例子进行说明。

  打开Visual Studio 2005,使用c#建立一个winform窗体应用程序,在窗体中添加一个label标签,一个文本框,一个按钮,如下图所示,我们要实现的功能是,在文本框输入一些信息后,点按钮,会弹出一个消息框,显示的是刚才输入的信息。

  假如我们编写的代码如下所示,出现了一个小错误,把textbox1.text的内容当作字符串的一部分了,所以显示不出用户输入的信息。

private void button1_Click(object sender, EventArgs e)
{
 //Show welcome message
 MessageBox.Show("Welcome textbox1.Text to Edit and Continue.");
}


  现在,如果我们在调试时遇到了这个错误,则可以使用edit and continue功能来进行改正。

  首先,按F7切换到代码视图,在MessageBox.Show这一行设置断点,之后按F5运行程序,当在文本框输入字符串时,由于设置了中断,光标停留在messagebox.show这行上,这时,我们可以修改代码,修改为:

private void button1_Click(object sender, EventArgs e)
{
 MessageBox.Show("Welcome " + textbox1.Text +" to Edit and Continue.");
}

  修改完后,继续按F5运行,这时候,则会显示正确的结果了,如下图,而不需要使用以往的"stop debugging"的功能先停止调试再修改程序。
   

   可视化调试器(Debugging Visualizers)

  在调试的时候,我们遇到的一个很麻烦的问题,就是有的时候很难看到某些参数的值。比如当你设置了一个断点去监测一个dataset,并且想看dataset里的每个datatable的数据,这在Visual Studio 2003中,是十分麻烦的,必须在auto/locals的监视窗口中,一层层地展开去看其值。在Visual Studio 2005中,为解决这个问题,新增加了叫"debugging visualizers"(可视化调试器)的功能,该功能可以在调试状态时,很容易方便地以各类形式,比如文本,html,xml等方式查看各种参数。

  在各类的监视窗口中,比如在autos,locals,watch和quickwatch 中都可以调用可视化调试器,方法是在某个需要监视的参数旁边,点击那个放大镜图标,就可以在打开的可视化调试器中看到该参数的详细情况,如下图所示:


  利用可视化调试器,就可以很方便地查看比如dataset类型的数据,如下图所示:
 
Data Tips

  在Visual Studio 2005中,在调试方面,还新增了"data tips(数据提示)"的功能。当在调试程序期间,只需要将光标移到代码中某个需要监视的参数上,系统就会自动显示与该参数相关联的一些很有用的信息,比如相关的属性,以及当前该参数的值。在Visual Studio 2003中,也有该功能,但提供的信息比现在的2005少很多。

  我们依然以上面"edit and continue"使用的例子来说明。我们将断点设在MessageBox.Show这一行,F5运行程序,当在文本框输入文字并按确定按钮后,程序自动中断,这时,将光标移到textbox1.text这个位置,系统就会调用data tips功能,如下图所示显示出与textbox文本框控件相关的一些属性以及这些属性此时对应的值。
  
 
  在data tip的功能中,还可以继续使用edit and continue的功能,只需要在需要的地方鼠标右键就可以弹出如下图的菜单,可以编辑该值。


   Exception Assistant(异常助手)

  在Visual Studio .net 2003中,当程序运行出现异常时,系统会显示一个异常信息对话框,询问是否停止程序执行或继续运行程序,但并没有提供给开发者更多的关于异常的信息。在Visual Studio .net 2005中,提供了 Exception Assistant(异常助手)的新功能,可以显示丰富的关于异常方面的信息,帮助开发者调试,如下图:
 

  在上图中,显示的是一个sqlException,是由于超时连接而造成的。在异常助手的窗口中,显示了关于该异常的详细信息,还可以将这些信息复制到剪贴版中,当选择view detail时,会弹出如下图所示的窗口,进一步显示关于该异常的详细信息:
 

   Just My Code Debugging

  在调试代码中,我们经常会遇到这样的问题,有时我们引用了一些其他工具包或者类库(如Microsoft提供的Enterprise Library),而在调试时,我们其实是不需要跟踪调试这些代码的,因为它们都已经证明是正确的。在Visual Studio 2003中,处理这类问题我们的方法一般是使用F10跳过它们,但一旦工程庞大的话,这样做十分麻烦。在Visual Studio 2005中,新增加了Just My Code debugging(JMC)的新特性,可以让开发者自己定制哪些代码是需要调试,哪些代码是永远不需要调试的,这样可以节省大量的时间。

  JMC功能由两方面来实现。首先,如果一个工程是以release方式进行编译构建的话(就是工程中,没有pdb文件),调试器会将其认为是非用户代码,那么在调试运行时,就根本不会进入该工程中进行调试。其次,可以使用System.Diagnostics命名空间里的DebuggerNonUserCodeAttribute属性,将其应用在希望不进行调试的代码段中。

  下面举一个例子来说明。下面的代码段中,有两个静态方法UserCode()和NonUserCode(),其中,在NonUserCode()中,是应用了DebuggerNonUserCode的属性,这表明这段代码将在调试运行时,调试器不会进入其中。将断点设置在其中的第9行,运行程序,程序会在第一个UserCode()里中断,现在试着用F11继续单步跟踪,会发现系统在进入usercode()方法中运行后,并没有进入到NonUserCode()里运行。

using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
// Step into F11 from here
UserCode();// Place break point on this line
NonUserCode();
UserCode();
}
static void UserCode()
{
Console.WriteLine("This is a call from user Code");
}
// Attribute to indicate the Debugger to jump
// over this method
[DebuggerNonUserCode]
static void NonUserCode()
{
Console.WriteLine("This is a call fron Non User Code");
}
}
编辑时对象识别

  在Visual Studio 2005中,新引入了"Object Identity While Debugging"的概念,也就是说,系统给在调试阶段的每个对象,都可以赋予一个别名,在调试时,直接引用该别名就可以了。

  假设在调试的时候,需要跟踪比如dataset或者hashtable等对象,这些对象中包含了很多其他的子对象,如果想对这些数量众多的子对象进行跟踪的话,将十分麻烦。在Visual Studio 2005中,可以使用object identity(对象标识)的方法去标记每一个对象,

  比如,在一个windows应用程序中,创建了一个dataset,并将其绑定到datagridview中去。如将sql server 的northwind数据库中的orders和orderdetail表中的数据读出填充到dataset里去,则在调试时,设置一个断点放在dataset里填充了数据之后的那行,这时,在监视窗口中,鼠标展开this.northwindData这个dataset,如下图所示,找到orders这个table,然后鼠标右键,会弹出一个菜单,选择其中的" Make Object ID".


  这时,会将orders这个参数以1#的别名方式命名(见下图),并且在整个应用程序中,只要在dataset的生存期,都可以以1#的方式访问它。


  下图的代码演示了如何在immediate监视窗口中,通过使用object 标识,查看某一个表中所有记录的方法。要注意的是,必须将其显式类型转换为DataApp.NorthwidDataSet.OrdersDataTable。通过使用object 标识的好处是,可以在调试阶段,不需要在描述某个参数时,再用冗余的命名方法表示了(比如可以用#1来代替this.NorthwidDataSet.Orders)


   小结

  在Visual Studio 2005中,在调试方面,增加了大量的方便实用的新特性,可以大大提高程序员调试的效率。除了本文提到的新特性外,Visual Studio 2005还可以搭配sql server 2005调试存储过程等,这将在稍侯的文章中做介绍。
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值