首先看一个简单的压缩还原程序:
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom<pbEnd) { b=*pbFrom++ if(b==bRepeatCode) { /**在pbTo开始的位置存储"size"个b */ b=*pbFrom++; size=(size_t)*pbFrom++; while(size-->0) *pbTo++=b; } else { *pbTo++=b; } /** 原文中以下代码没有被注释,个人感觉有问题 */ // return pbTo; } /** 原文中没有这行代码 */ return pbTo; }
仔细思考一下,上面一个程序进行一次译码,总共需要三个字节。所以压缩程序不应该对两个连续的字符进行压缩,当然对连续三个字符进行压缩也没有什么好处。所以压缩程序应该只对连续三个以上的字符进行压缩。还有一个情况,就是如果原始数据中含有bReatCode,就必须对其进行特殊处理,否则解压程序会误以为它是一个压缩字符序列的开始。所以当原始数据中出现了bReatCode时,就把它再重复一次,以便和真正的压缩字符序列区别。
所以我们可以使用断言来对这两个特性进行检验:
ASSERT(size>=4||(size==1&&b==Reaptcode));
如果断言失败说明pbFrom所指向的数据有问题或者压缩程序有问题。所以利用断言,我们不仅可以检查语法上不可能发生的情况,而且可以利用断言来检验程序逻辑上不可能发生的错误。
利用断言来检查不可能发生的情况。
让我们接下来看字符解压程序的另一个版本:
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom!=pbEnd) { b=*pbFrom++ if(b==bRepeatCode) { /**在pbTo开始的位置存储"size"个b */ b=*pbFrom++; size=(size_t)*pbFrom++; /** 检查原始数据的有效性 */ ASSERT(size>3||(size==1&&b==bReaptCode)); do *pbTo++=b; /** 原文是 while(size--!=0), 个人感觉有问题,因为会多循环一次 于是改成如下语句 */ while(--size!=0); } else { *pbTo++=b; } /** 原文中以下代码没有被注释,个人感觉有问题 */ // return pbTo; } /** 原文中没有这行代码 */ return pbTo; }
似乎第一个版本更加合理,也更加聪明,但是如果出于某种原因pbFrom被加过了pbEnd,第一个版本的程序可以在程序造成过多的损害之前,它就会退出,而第二个版本的程序则会企图对整个内存中的内容进行解压,从而引起程序崩溃,用户肯定会发现这个错误。所以实际情况就是这样:防错性程序设计虽然常常被誉为有较好的编码风格,但是它却隐瞒了错误。但是这并不意味者我们应该放弃防错性程序设计。我们希望在进行防错性程序设计时,错误不要被隐瞒。所以对于上面的程序,我们可以一方面一如既往地使用防错性程序设计,另一方面在事情变槽的情况下利用断言进行报警。
byte* pbExpand(byte *pbFrom,byte *pbTo,size_t sizeFrom) { byte b, *bpEnd; size_t size; pbEnd=pbFrom+sizeFrom; while(pbFrom<pbEnd) { b=*pbFrom++ if(b==bRepeatCode) { /**在pbTo开始的位置存储"size"个b */ b=*pbFrom++; size=(size_t)*pbFrom++; while(size-->0) *pbTo++=b; } else { *pbTo++=b; } /** 原文中以下代码没有被注释,个人感觉有问题 */ // return pbTo; } ASSERT(pbFrom==pbEnd); /** 原文中没有这行代码 */ return pbTo; }
在进行防错性程序设计时,不要隐瞒错误。
同时,在编写代码时,要抓住一切机会对程序的结果进行验证。要尽可能地使用不同的算法,而且要使其不仅仅是同一算法的又一实现。如果不同算法产生的结果不同,就会触发断言。通过使用不同的算法不仅可以发现算法实现中的错误,而且增加了发现算法本身错误的可能性。当然这并不意味着每个函数都得有两个版本,正确的做法是只对程序的关键部分这样做。
要利用不同的算法对程序的结果进行确认。
尽管利用不同算法的执行结果来对程序进行确认,可以帮助我们发现程序中的错误。但这毕竟要等到算法执行结束之后才能发现错误。有时候程序员应该在程序中进行初始检查,这样可以尽快发现错误,否则错误会隐藏一段时间。
不要等待错误发生,要使用初始检查程序。
总结:
1,防错性程序设计会隐瞒错误。当进行防错性编码时如果“不可能发生”的情况确实发生了,要使用断言报警。
2,利用不同的算法对程序结果进行确认,当同一问题的不同算法出现不同结果时,触发断言。
3,使用初始检查程序,尽早发现程序中的错误。
最后以作者的一句话结束这篇文章:
测试者的工作并不只是针对你的程序进行测试,查出自己程序中的错误毕竟是你自己的工作。
编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二),布布扣,bubuko.com
编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二)
原文地址:http://blog.csdn.net/fuchencong/article/details/26001317