跟踪与中间过程输出

本文介绍了在Visual C++开发环境中使用断点及TRACE宏进行程序调试的方法。包括如何设置断点进行跟踪,利用TRACE宏输出中间结果,以及使用转储(Dump)展示对象内部状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟踪与中间过程输出
program 发表于 2006-1-24 15:27:33
 
  也许一个开发人员一半以上的时间都是在面对错误,所以好的调试/查错方法(工具)会减轻我们工作的负担,也可以让枯燥的DEBUG过程得以缩短。
  VC开发环境所提供的调试环境是很优秀的,我们可以运用单步运行,设置断点的方法来查找问题所在。但是这种跟踪是非常耗时的,所以我们需要采用一些策略来让我们更容易的发现错误并对错误进行定位,所幸的是VC在这方面提供了强大的支持。在本节中我们先看看如何利用设置断点和利用TRACE宏来输出运行情况。
  在VC开发环境中按下F9就可以在光标所在行设置断点,再按一次就可以取消该处断点。设置断点的意义在于在调试过程当运行到该行时回产生一个中断并返回到VC开发环境中,在开发环境中你可以查看各个变量的值。下面是我们用于测试的代码,前面有红色圆形的行表示该行设置有断点:
一般来讲设置断点有下面的技巧:
  • 设置在进行判断的代码处,这样可以在运行时可以观察判断所依赖的条件是否正确。
  • 设置函数开始处,观察该函数所依赖的变量是否都设置正确。
  • 设置函数结束处,观察该函数对变量的改变是否正确。
  • 设置进入其他函数前/后,通过黑盒法检查该函数功能是否正确。
  • 对于循环体,应该先测试一个循环次数小的条件来检查循环逻辑是否正确,或者在循环的前几次设置断点,在运行几次后取消断点。
在调试过程中到达断点处你可以通过上下文变量窗口(Variables)观察该函数中的变量的值,如果需要观察未在该函数出现的全局变量或者类成员变量这需要将变量名添加到观察窗口(Watch)中输入变量名称。但程序编译完成后请按下F5键以调试的方式执行程序,当进入断点时VC开发环境会被自动激活,然后我们可以可以观察程序的运行情况。在调试过程中也可以添加和删除断点。如下图:
  如果在运行过程中被观察的变量的值发生了变化则该变量在观察窗中会变为红色。
MFC中提供的TRACE宏可以帮助我们在程序调试运行过程中方便的输出调试信息。TRACE宏的定义为:TRACE(exp),其中的表达式使用与printf相同的表达方法。例如下面的代码:
void CSam_sp_31Dlg::OnTest2()
{
         static int i=5,j=50;
         char szDeb[]="debug string";
         TRACE("trace i=%d j=%d/nstring=%s/n",i,j,szDeb);
         i+=1;
         j+=5;
}
  在以调试方式运行程序是,当你点击TRACE按钮时会看到在调试窗口中输出了调试信息。
  当程序在调试过程中执行到此处时会在输出窗口输入"trace i=5 j=50/n,string=debug string/n"。使用TRACE宏可以让我们随时掌握程序运行过程中变量的变化情况,因为大多数情况下我们都不希望使用断点进入到程序内部,而只是注意运行中数据的值。
注意:不要采用TRACE宏一次性输出大批量数据或不间断输出数据,因为这样有可能会时程序运行变得非常缓慢,如:
void test_trace_e(void)
{
         char *pszDeb=new char[1024*1024];
         TRACE("%s/n",pszDeb);
         //或者
         for(int i=0;i<sizeof(pszDeb);i++)
         {
                 TRACE("%c/n",pszDeb[i]);
         }
}
  有一点需要注意的是,TRACE宏在只在调试(DEBUG)版本中起作用,而在发行(RELEASE)版本无效,所以不要在TRACE宏中进行对程序状态进行改变的计算或是调用对状态有改变的函数,例如:
void yourClass::fun1()
{
         TRACE("%d",++m_iTick); //m_iTick状态改变
         TRACE("return value = %d",DoSomething());
}
void yourClass:DoSomething()
{
         if(m_szOut == "No")
         {
                 return FALSE;
         }
         else
         {
                 m_szOut="Yes"; //状态改变
                 reutrn TRUE;
         }
}
在调试中还有一种方法可以将对象内部内容输出到调试窗口中,这就是使用转储(Dump)。转储的实现要通过对象自身实现,在通过对象自身实现时有一个好处就在于可以输入内部受保护层成员。首先CObject类定义了虚函数:virtual void Dump( CDumpContext& dc ) const;当你从CObject中派生新类时你需要重载该函数,例如下面是个很简单的例子:
class CMyButton : public CButton
{
public:
         CMyButton();
         ~CMyButton(){};
public:
#ifdef _DEBUG 
         //由于转储只在调试版本中实现,所以使用条件编译
         virtual void Dump( CDumpContext& dc ) const;
#endif
protected:
         CString m_szHotText;//当鼠标移动过显示的文字
};
 
CMyButton::CMyButton():CButton()
{
}
 
#ifdef _DEBUG
void CMyButton::Dump( CDumpContext& dc ) const
{
         dc<<"/n";
         CButton::Dump(dc);
         dc<<"/ndump of CMyButton /ntext is "<<m_szHotText;
         dc<<"/n";
}
#endif
  我们看到Dump函数接受一个参数为CDumpContext,通过该类可以将数据输出到调试窗口或是文件。CDumpContext重载了<<操作符,利用<<可以输出各种类型的数据。
下面的代码示范了调用方法:
void CSam_sp_31Dlg::OnDump() 
{
         CButton bu1;
         CMyButton bu2;
#ifdef _DEBUG
         //由于转储只在调试版本中实现,所以使用条件编译
         bu1.Dump(afxDump); //afxDump是一个CDumpContext类型的全局变量。
         bu2.Dump(afxDump);
#endif
}
//输出的调试信息为:

a CButton at $64F538
m_hWnd = 0x0
 
a CButton at $64F4F4
m_hWnd = 0x0
dump of CMyButton 
text is not init

//上面两行输出了CButton的转储信息,后面四行输出了CMyButton的转储信息。
  设置断点进行跟踪和输出中间结果是最基本的调试方法,也是必须掌握的技巧。
### Java 中间输出方法 在 Java 编程中,中间输出通常用于调试目的,以便开发者能够查看程序运行过程中的变量状态或逻辑流程。以下是几种常见的实现方式: #### 1. 使用 `System.out` 打印中间结果 最简单的方式是通过 `System.out.println()` 或 `System.out.print()` 来打印中间结果。这种方式适用于快速验证某些变量的值或者跟踪程序执行路径。 ```java public class MiddleOutputExample { public static void main(String[] args) { int a = 5; int b = 10; // 打印中间计算结果 System.out.println("Before calculation, a=" + a + ", b=" + b); int result = add(a, b); // 继续打印最终结果 System.out.println("After calculation, the result is: " + result); } private static int add(int x, int y) { int sum = x + y; System.out.println("Inside add method, intermediate value of sum: " + sum); // 中间输出 return sum; } } ``` 上述代码展示了如何利用 `System.out.println()` 在函数内部打印中间结果[^1]。 --- #### 2. 调试工具日志框架 对于更复杂的项目,建议使用专业的日志记录库(如 Log4j、SLF4J),这些工具提供了灵活的日志级别管理功能,便于开发人员根据需求调整输出内容。 ##### 示例:Log4j 配置 引入依赖并配置好后,可以通过以下方式进行中间结果输出: ```java import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; public class LoggingExample { private static final Logger logger = LogManager.getLogger(LoggingException.class.getName()); public static void main(String[] args) { try { int num = divide(10, 0); logger.info("Division successful! Result={}", num); } catch (ArithmeticException e) { logger.error("Error during division", e); } } private static int divide(int numerator, int denominator) { if (denominator == 0) { throw new ArithmeticException("Denominator cannot be zero"); } int quotient = numerator / denominator; logger.debug("Intermediate computation completed with quotient={}", quotient); return quotient; } } ``` 此示例说明了如何借助日志框架完成更加结构化的中间结果输出[^4]。 --- #### 3. 自定义对象的字符串表示形式 当需要打印复杂的数据结构时,重写其 `toString()` 方法是一个不错的选择。这使得即使是在简单的 `System.out.println()` 下也能获得清晰可读的信息。 ```java class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } public class ToStringExample { public static void main(String[] args) { Person person = new Person("Alice", 30); System.out.println(person.toString()); // 输出定制化后的对象描述 } } ``` 这里展示了一个例子,其中自定义类实现了自己的 `toString()` 方法来提供更好的可视化效果[^3]。 --- #### 总结 无论是基础级别的 `System.out.println()` 还是高级别的日志解决方案,在实际应用过程中都需要权衡效率易用性之间的关系。合理选用合适的手段可以帮助提高编码质量以及排查潜在错误的能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值