将程序错误消灭在萌芽中

[入库:2005年8月18日] [更新:2007年3月24日]

本文简介:选择自 bugfree 的 blog

--------------------------------------------------------------------------------

这篇文章贡献自alex rest, 翻译: bugfree/csdn
平台: vc6, vc7, windows 9x/nt/2000/xp

--------------------------------------------------------------------------------

我同许多学生接触很多, 很好的了解他们的错误. 而且, 我也很了解我的错误. 有一种能使你的程序可信度更高. 它就是应用assertiong宏的陷阱(trap)技术. 下面描述了通常的c++技术和vc++/mfc特殊功能.

1. 为什么必须将指针初始化为 null
如果你有

class a{
public:
  a();
  ~a();
  //some declarations.
private:
  sometype* m_ppointer;
  //some declarations.
};

a::~a(){
  delete m_ppointer;
}

如果你在构造函数之外给指针赋值, 你必须总是将它设为null. 例如:

a::a():m_ppointer(null){}

—为什么?
—因为:

  1). 如果你的程序执行的逻辑不为指针创建对象, delete操作将无错的执行(根据c++语言标准). 如果你忘了初始化bypass pointer, 它将有一个随机值, 对这个地址的删除将是你的程序崩溃.
  2). 通过用断点和追踪, 你能够容易的找到没有初始化的指针.
  3). 你可以用 "if" 操作来测试一个有效地指针, 如:
if(m_ppointer){
  m_ppointer->dosomething();
}
else{
  afxmessagebox("unexpected error #1234. send me letter
                 please to aaa@bbb.com");
}
 
  可能你的程序不能正确工作, 但是在这里它不会崩溃. 这是非常重要的. 想象你已经输入了两个小时文本并没有存盘. 你更希望文本编辑器崩溃呢还是给出一些警告?

  4).你可以用assert宏设置调试陷阱(debug trap)

2. 为调试设的陷阱(traps)

插入断点是一个好技术, 但是如果问题出在长的循环内部那就不是高效的了. 例如, 在10,000次循环之后,一些条件变的有问题了.  为了抓住这样的问题, vc++/mfc 程序员应用 assert 宏. ansi宏也经常被应用.用哪个不是问题, 在下面的例子中我用了 mfc assert 宏. 

—它怎样被用?
—这样:

assert(condition);
例子:

assert(nwatertemperature > 0 &&
       nwatertemperature < 100);    // break if wrong value
assert(psomepointer);               // break if null pointer
assert(0);                          // break if here

—它怎样工作?
—这样:

断言(assertion)用带断言信息(程序, 模块, assertion行)的对话框执行. 对话框有3个按钮: "break", "repeat" ("debug"), and "continue" ("ignore"). "break" 结束程序, "continue" 忽略断言, 最有用的是"repeat"按钮. 按下它在断言的地方打开源代码编辑器. 在这里你可以测试所有的变量值并明白哪里出了问题.

—它怎样用?
—多数这样用:

为了控制传进的指针:
void somefun(sometype* ppointer)
{
  assert(ppointer);
  //some instructions.
}

你可以在"switch" 和 "if"操作中捕获奇怪的值
例如:

switch(nrgbcolors){
  case nred:   {//some instructions.} break;
  case ngreen: {//some instructions.} break;
  case nblue:  {//some instructions.} break;
  default: assert(0);    // we should have never come here!
}

if(nwatertemp >=0 && nwatertemp < 50){
  //some instructions.
}
else if(nwatertemp >= 50 && nwatertemp <= 100){
  //some instructions.
}
else{
  assert(0);    // we should have never come here!
}

对值的断言:
assert(nsomevalue >= minvalue and nsomevalue <= maxvalue);
assert(nothervalue != 0);
等.

always use this technique and you will be greatly surprised how often such traps will work!
总是应用这个技术, 你将被how often这些陷阱将工作  大大震惊.

3. 可爱的 assert 错误
assert( m_mywnd.create() );

呕! 这是一个可怕的错误! 程序在调试版中正常工作, 在发行版中不工作. 记住: 这是一个在发行版中将被移除的宏. 以这种方法你的窗口将永远不会被创建. 如果你用 mfc, 这样做:

verify( m_mywnd.create() );

它在调试版中像assert一样并且在发行版中执行m_mywnd.create()

4. 对象验证及mfc宏assert_valid
利用类的verify成员是众所周知的验证对象的技术. 如果你有明确的对象的验证条件, 你可以创建和利用verify类成员.
例如:

class time
{
public:
  void set(int h, int m);
  bool verify(){return m_h>=0 && m_h<24 and m_m>=0 && m_m<60; }
  //some instructions.
};

void time::set(int h, int m)
{
  m_h = h;
  m_m = m;
  assert(verify());
}


大多数的mfc类是cobject的子类. 它有用来验证用的assertvalid虚函数 如果一个类实现了这个函数, 他被assert_valid宏调用. 例如:
assert_valid(pview);

它检查某个cviewwnd对象的指针. 如果对象无效(空指针或错的窗口句柄), 断言将被执行.

5. mfc trace 宏

没有trace宏描述的mfc宏的附录将是不调和的. 在主控台模式下用流输出变量的值是没有问题的. 从另一方面来讲, windows下编程追踪变量并不是一个琐碎的任务.实际上, 当我们追踪某事时许多窗口可以被打开和关闭. 没有必要把追踪输出写到许多窗口上. 一个窗口足够了. 因为这个目的, vc++ ide应用了"输出"窗口(view-output menu point). 为了调试输出, 你可以利用trace操作符, 它同printf stdio函数具有相同的格式.

例如:

trace("\nthis is a trace of int variable %d.", nsomeint);
trace("\nfunction oninitialupdate is starting.");

链接:
我的其它文章, demo project, 和许多有趣的c++链接可以在这里找到:
http://www.brigsoft.com/edu

我的软件在这里:
http://www.brigsoft.com.

 

本文关键:调试技术 ASSERT TRACE
 

本站最佳浏览方式为 分辨率 1024x768 IE 6.0(或更高版本的 IE浏览器)

go top