断言式编程

断言式编程

 

       在自责中有一种满足感。当我们责备自己时,会觉得再没人有权责备我们。

 

                                          ——奥斯卡 · 王尔德:《多里安 ·格雷的画像》.

 

    每一个程序员似乎都必须在其职业生涯的早期记住一段曼特罗(mantra)。它是计算技术的基本原则,是我们学着应用于需求、设计、代码、注释——也就是我们所做的每一件事情——的核心信仰。那就是:

 

   这决不会发生……

 

    “这些代码不会被用上30年,所以用两位数字表示日期没问题。”“这个应用决不会在国外使用,那么为什么要使其国际化?”“count不可能为负。”“这个 printf不可能会失败。”

 

    我们不要这样自我欺骗,特别是在编码时。

 

If it Can’t Happen , Use Assertions to Ensure That it Won’t!

 

如果它不可能发生,用断言确保它不会发生!

 

无论何时你发现自己在思考“但那当然不可能发生”,增加代码检查它。最容易的办法是使用断言。在大多数 C C++ 实现中,你都能找到某种形式的检查布尔条件的 assert _assert 宏。这些宏是无价的财富。如果传入你的过程的指针决不应该是 NULL ,那么就检查它:

 

void writeString (char *string){

 

      assert (string != NULL);

 

      …

 

对于算法的操作,断言也是有用的检查。也许你编写了一个聪明的排序算法。检查它是否能工作:

 

for ( int I=0; I
      assert (sorted[i] <= sorted[i+1]);

 

}

 

当然,传给断言的条件不应该有副作用。还要记住断言可能会在编译时被关闭——决不要把 必须 执行的代码放在 assert 中。

 

不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情:你不会想编写这样的代码:

 

printf( “Enter ‘Y’ or ‘N’ : “);

 

ch = getchar();

 

assert((ch ==’Y’) || (ch ==’N’));              /*bad idea!*/

 

而且,提供给你的 assert 宏会在断言失败时调用 exit ,并不意味着你编写的版本就应该这么做。如果你需要释放资源,就让断言失败生成异常、 longjump 到某个退出点、或是调用错误处理器。要确保你在终止前的几毫秒内执行的代码不依赖最初触发断言失败的信息。

 

让断言开着

 

有一个由编写编译器和语言环境的人传播的、关于断言的常见谅解。就是像这样的说法:

 

断言给代码增加了一些开销。因为它们检查的是决不应该发生的事情,所以只会由代码中的 bug 触发。一旦代码经过了测试并发布出去,它们就不再需要存在,应该被关闭,以使代码运行的更快。断言是一种调试设施。

 

这里有两个明显错误的假定。首先,他们假定测试能够找到所有的 bug 。现实的情况是,对于任何复杂的程序,你甚至不大可能测试你的代码执行路径的排列数的极小一部分。其次,乐观主义者们忘记了你的程序运行在一个危险的世界上。在测试过程中,老鼠可能不会咬坏通信电缆、某个玩游戏的人不会耗尽内存、日志文件不会塞慢硬盘。这些事情可能会在你的程序运行在实际工作环境中时发生。你的第一条防线是检查任何可能的错误,第二条防线是使用断言设法检测你疏漏的错误。

 

在你把程序交付使用时关闭断言就像是因为你曾经成功过,就不用保护网去走钢丝。那样做有极大的价值,但却难以获得人身保险。

 

即使你确实有性能问题,也只关闭那些真的有很大影响的断言。上面的排序例子也许是你应用的关键部分,也许需要很快才行。增加检查意味着又一次通过数据,这可能让人不能接受。让那个检查成为可选的,但让其余的留下来。

 

断言与副作用

 

如果我们增加的错误检测代码实际上却制造了新的错误,那是一件让人尴尬的事情。如果对条件的计算有副作用,这样的事情可能会在使用断言时发生。例如,在 java 中,像下面这样编写代码,不是个好注意:

 

while (iter.hasmoreElements () {

 

      Test.ASSERT(iter.nextElements() != null);

 

      Object obj = iter.nextElement();

 

      //….

 

}
ASSERT 中的 .nextElement() 调用有副作用:它会让迭代器越过正在读取的元素,这样循环就会只处理集合中的一半元素。这样编写代码会更好:

 

while (iter.hasmoreElements () ){

 

      Object obj = iter.nextElement();

 

      Test.ASSERT(obj != null);

 

      //….

 

}

 

这个问题时一种“海森堡虫子”(Heisenbug)——调试改变了被调试系统的行为。
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值