笔记:如何用好断言(ASSERT),且看经典是怎么说的吧

从事软件开发快五年了,走读代码经常可以看到没有合理使用断言的现象,而且每当把关于断言的话题提出来的时候,似乎总会引起争议。

这并不算很有吸引力的话题,因为好些名著都已经就此做过阐述,在网络上也广为流传了,书摘如下。

其中我推荐《C语言接口与实现》和《编程精粹》两本书关于断言的使用说明尤其值得仔细阅读。

 

《代码大全》

5.6.l   使用断言

断言是一个在假设不正确时会大声抗议的函数或宏指令。可以使用断言来验证在程序中作出的假设并排除意外情况。在开发阶段,断言可以消除相互矛盾的假设,消除传入子程序的不良数值等等。在维护阶段,可以表明改动是否影响到了程序其它部分。如果在开发阶段使用预处理程序处理断言,那么在最终代码中把断言去掉是非常容易的。不过因此在断言中应避免使用可执行代码,把可执行代码放入断言,因为在关闭断言时,它将不会被编译。

 

5.7 子程序参数

说明参数的接口假设。如果假定被传入子程序的数据具有某种特性,那么需要对这个假设作出说明。在子程序中和在调用程序的地方都需要对这一假设进行注释。如果能在代码中放入断言的话,那么其效果要好于注释。

 

《编程珠玑》

5.3 断言的艺术

某些项目使用预处理器定义断言,于是可以在编译阶段处理断言,而不会导致运行时的额外开销。另一方面,Tony Hoare曾经注意到,在测试时使用断言,而在产品发布时将断言关闭的程序员,就像是在岸上操练时穿着救生衣,而下海时将救生衣脱下的水手。

 

Clean Code

It’s good documentation, but it doesn’t solve the problem. If someone passes null, we’ll

still have a runtime error.

(笔记:断言也有注释的作用,但它不解决问题。)

 

《C语言接口与实现》David R. Hanson

 2.4 客户调用程序的责任

(待补充:这一节从接口设计的角度说明断言的适用场合,是我见过的最清晰的讲解!) 

第四章 异常与断言

(暂略)

 

《编程精粹(Writing Clean Code)》

 

2   自己设计并使用断言

利用编译程序自动查错固然好,但我敢说只要你观察一下项目中那些比较明显的错误,

就会发现编译程序所查出的只是其中的一小部分。编译程序查不出算法的错误,无法验

证程序员所作的假定。更一般地,编译程序也查不出所传递的参数是否有效。

 

要点:

 

@要使用断言对函数参数进行确认。

使用断言对函数的参数进行确认,并且在程序员使用了无定义的特性时向程序员报警。函数定义得越严格,确认其参数就越容易。

 

@在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?”一旦确定

了相应的假定,就要使用断言对所做的假定进行检验,或者重新编写代码去掉相应

的假定。另外,还要问:“这个程序中最可能出错的是什么,怎样才能自动地查出

相应的错误?”努力编写出能够尽早查出错误的测试程序。

 

@要从程序中删去无定义的特性或者在程序中使用断言来检查出无定义特性的非法使用。

 

如果读者停下来读读 ANSI C memcpy 函数的定义,就会看到其最后一行说:“如果在存储空间相互重叠的对象之间进行了拷贝,其结果无定义”。确实有些程序员在故意地使用无定义的特性,但我们不应该效仿。无定义的特性就相当于非法的特征,因此要利用断言对其进行检查。

 

所以从今以后,要经常停下来看看程序中有没有使用无定义的特性。如果程序中使用了

无定义的特性就要把它从相应的设计中去掉,或者在程序中包括相应的断言,以便用户在使用了无定义的特性时,能够向程序员发出通报。

 

@给不够清楚的断言加上注解。

这就好比一个人在穿过森林时,看到树上钉着一

块上书“危险”红字的大牌子。但危险到底是什么?树要倒?废矿井?大脚兽?除非告诉人

们危险是什么或者危险非常明显,否则这个牌子就起不到帮助人们提高警觉的作用,人们会

忽视牌子上的警告。同样,程序员不理解的断言也会被忽视。在这种情况下,程序员会认为

相应的断言是错误的,并把它们从程序中去掉。因此,为了使程序员能够理解断言的意图,

要给不够清楚的断言加上注解。

 

@不是用来检查错误的

当程序员刚开始使用断言时,有时会错误地利用断言去检查真正地错误,而不去检查非

法的况。看看在下面的函数 strdup 中的两个断言:

char* strdup(char* str)

{

  char* strNew;

  ASSERT(str != NULL);

  strNew = (char*)malloc(strlen(str)+1);

  ASSERT(strNew != NULL);

  strcpy(strNew, str);

  return(strNew);

}

第一个断言的用法是正确的,因为它被用来检查在该程序正常工作时绝不应该发生的非

法情况。第二个断言的用法相当不同,它所测试的是错误情况,是在其最终产品中肯定会出

现并且必须对其进行处理的错误情况。

 

(笔记:此处所言“错误”指的是《C语言接口与实现》一书所言的“异常”。)

 

@在进行防错性程序设计时,不要隐瞒错误

在编码之前都要问自己:“在进行防错性程序设计时,程序中隐瞒错误了吗?”如果答案是肯定的,就要在程序中加上相应的断言,以对这些错误进行报警。

  

@避免过度使用

断言也可能被过度使用,用户要自己灵活掌握断言的使用分寸。例如对于某些程序员来说,每次相除都利用断言检查分母是否为 0 可能很重要,但对其它的程序员来说,这可能很可笑。用户要自已权衡。

 

@不要混淆非法情况与错误情况之间的区别

要使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,前者是表明代码违法(即《C接口》所说的契约),后者是在最终产品中必须处理的。

  

Beginning.Linux.Programming 3rdNeil Matthew ,Richard Stones

 

Chapter 10: Debugging

Assertions

While it’s common to introduce debug code such as printf calls, possibly by conditional compilation,

during the development of a program, it’s sometimes impractical to leave these messages in a delivered

system. However, it’s often the case that problems occur during the program operation that are related

to incorrect assumptions rather than coding errors. These are events that “can’t happen.” For example, a

function may be written with the understanding that its input parameters will be within a certain range.

If it’s passed incorrect data, it might invalidate the whole system.

For these cases, where the internal logic of the system needs to be confirmed, X/Open provides the

assert macro that can be used to test that an assumption is correct and halt the program if not.

#include <assert.h>

void assert(int expression)

The assert macro evaluates the expression and, if it’s nonzero, writes some diagnostic information to

the standard error and calls abort to end the program

 

The header file assert.h defines the macro depending on the definition of NDEBUG. If NDEBUG is

defined when the header file is processed, assert is defined to be essentially nothing. This means that

you can turn off assertions at compile time by compiling with -DNDEBUG or by including the line

#define NDEBUG

in each source file before including assert.h.

This method of use is one problem with assert. If you use assert during testing, but turn it off for

production code, your production code could have less safety checking than when you are testing it.

Leaving assertions enabled in production code is not normally an option—would you like your code to

present a customer with the unfriendly error assert failed and a stopped program? You may consider

it better to write your own error trapping routine that still checks the assertion but doesn’t need to be

completely disabled in production code.

You must also be careful that there are no side effects in the assert expression. For example, if you use a

function call with a side effect, the effect won’t happen in the production code if assertions are removed.

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值