Visual C++程序调试方法入门

概述

  调试是一个程序员最基本的技能,其重要性甚至超过学习一门语言。不会调试的程序员就意味着他即使 会一门语言,却不能编制出任何好的软件。

  这里我简要的根据自己的经验列出调试中比较常用的技巧,希望对大家有用。

  本文约定,在选择 菜单时,通过 / 表 示分级菜单,例如 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

没有调试信息

/Zd

Line Numbers Only

目标文件或者可执行文件中只包含全局和导出符号以及代码行信息,不包含符号调试信息

/Z7

C 7.0- Compatible

目标文件或者可执行文件中包含行号和所有符号调试信息,包括变量名及类型,函数及原型等

/Zi

Program Database

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

/ZI

Program 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
  • 选择不使用预编译 头。


coder_jack@126.com
日记写到 200903091721

阅读更多
个人分类: CPP
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭