编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二)
接着上一遍文章<<编程精粹--编写高质量C语言代码(2):自己设计并使用断言(一)>>,继续学习怎样自己设计并使用断言,来更加easy,更加不费力地自己主动寻找出程序中的错误。
首先看一个简单的压缩还原程序:
- 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;
- }
以上这个程序不是原书中的程序。个人感觉原书中的程序有问题,所以进行了一些小改动。
这个程序的一个关键点就是假设在输入数据中找到了bReapeatCode,它就觉得其后的两个字节分别代表反复的还原字符以及该字符的反复次数。依照上一篇文章<<编程精粹--编写高质量C语言代码(2):自己设计并使用断言(一)>>讲过的。为了提高程序的健壮性,能够利用断言对函数參数的有效性进行检查。
除此之外,事实上还有更多事情能够做,比如对缓冲区的数据进行确认。
细致思考一下,上面一个程序进行一次译码,总共须要三个字节。所以压缩程序不应该对两个连续的字符进行压缩,当然对连续三个字符进行压缩也没有什么优点。所以压缩程序应该仅仅对连续三个以上的字符进行压缩。另一个情况。就是假设原始数据中含有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;
- }
细致观察这两个程序。虽然功能是一样的。可是第一个版本号利用了防错性程序设计。我们能够分析一下,外层循环虽然不太可能出现pbEnd会大于pbFrom,可是一旦出现。程序的第一个版本号会跳出外层循环,继续执行,而第二个版本号程序则可能崩溃。相同对于内层循环,一旦出现size为0的情况,第一个版本号的程序能够非常好的退出循环。而第二个版本号则无法做到。
似乎第一个版本号更加合理,也更加聪明。可是假设出于某种原因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;
- }
ASSERT(bpFrom==pbEnd)用来验证函数的正常终止。因为採用了对应的防错性程序设计,程序的交付版本号能够保证出了毛病时用户不受损失,而在程序的调试版本号中,错误仍然能够被报告出来。
所以在编码之前都要问自己:“在进行防错性程序设计时,程序中隐瞒了错误吗?”假设答案是肯定的,就要在程序中加上断言,以对这些错误进行报警。
在进行防错性程序设计时,不要隐瞒错误。
同一时候。在编写代码时,要抓住一切机会对程序的结果进行验证。
要尽可能地使用不同的算法。并且要使其不不过同一算法的又一实现。
假设不同算法产生的结果不同,就会触发断言。通过使用不同的算法不仅能够发现算法实现中的错误,并且添加了发现算法本身错误的可能性。当然这并不意味着每一个函数都得有两个版本号。正确的做法是只对程序的关键部分这样做。
要利用不同的算法对程序的结果进行确认。
虽然利用不同算法的运行结果来对程序进行确认。能够帮助我们发现程序中的错误。但这毕竟要等到算法运行结束之后才干发现错误。有时候程序猿应该在程序中进行初始检查,这样能够尽快发现错误。否则错误会隐藏一段时间。
不要等待发生错误,要使用初始检查程序。
总结:
1,防错性程序设计会隐瞒错误。当进行防错性编码时假设“不可能发生”的情况确实发生了,要使用断言报警。
2,利用不同的算法对程序结果进行确认,当同一问题的不同算法出现不同结果时。触发断言。
3,使用初始检查程序,尽早发现程序中的错误。
最后以作者的一句话结束这篇文章:
測试者的工作并不仅仅是针对你的程序进行測试,查出自己程序中的错误毕竟是你自己的工作。
编程精粹--编写高质量C语言代码(3):自己设计并使用断言(二)的更多相关文章
- 编程精粹--编写高质量C语言代码(4):为子系统设防(一)
通常,子系统都要对事实上现细节进行隐藏,在进行细节隐藏的同一时候.子系统为用户提供了一些关键入口点. 程序猿通过调用这些关键的入口点来实现与子系统的通信.因此假设在程序中使用这种子系统而且在其调用点加 ...
- <编程精粹:编写高质量C语言代码> 读书笔记
0.规则<The Elements of Programming Style><The Elements of Style> 1.假想的编译程序(1)使用编译器提供的所有的可选 ...
- 编程精粹--编写高质量C语言代码(1):假想编译程序
编译程序只能查找出程序的语法错误,而对于"数组越界訪问","对空指针解引用"等错误.编译程序是束手无策的.同一时候我们知道測试人员所使用的黑箱測试方法所能做的不 ...
- HTML Inspector – 帮助你编写高质量的 HTML 代码
HTML Inspector 是一款代码质量检测工具,帮助你编写更优秀的 HTML 代码.HTML Inspector 使用 JavaScript 编写,运行在浏览器中,是最好的 HTML 代码检测工 ...
- iOS应用开发最佳实践系列一:编写高质量的Objective-C代码
本文由海水的味道编译整理,转载请注明译者和出处,请勿用于商业用途! 点标记语法 属性和幂等方法(多次调用和一次调用返回的结果相同)使用点标记语法访问,其他的情况使用方括号标记语法. 良好的 ...
- 如何编写高质量的js代码--底层原理
转自: 如何编写高质量的 JS 函数(1) -- 敲山震虎篇 本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/7lCK9cHmunvYlbm ...
- 如何编写高质量的C#代码(一)
从"整洁代码"谈起 一千个读者,就有一千个哈姆雷特,代码质量也同样如此. 想必每一个对于代码有追求的开发者,对于"高质量"这个词,或多或少都有自己的一丝理解.当 ...
- 怎样编写高质量的java代码
代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友 ...
- 怎样编写高质量的 Java 代码
代码质量概述 怎样辨别一个项目代码写得好还是坏?优秀的代码和腐化的代码区别在哪里?怎么让自己写的代码既漂亮又有生命力?接下来将对代码质量的问题进行一些粗略的介绍.也请有过代码质量相关经验的朋友提出宝贵 ...
随机推荐
- windows echo命令
ECHO命令是大家都熟悉的DOS批处理命令的一条子命令,但它的一些功能和用法也许你并不是全都知道,不信你瞧: 1. 作为控制批处理命令在执行时是否显示命令行自身的开关 格式:ECHO [ON|OFF ...
- vue-cropper
项目中用到了vue-cropper插件,让我觉得很好用附上两个地址 vue-cropper在git上的地址 https://github.com/xyxiao001/vue-cropper 针对vue ...
- org.springframework.orm.hibernate4.support.OpenSessionInterceptor
/* * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Vers ...
- 深入理解python对象及属性
类属性和实例属性首先来看看类属性和类实例的属性在python中如何存储,通过__dir__方法来查看对象的属性 >>> class Test(object): pass>> ...
- SqlServer2012学习 - 基本数据类型认知
精确数字: 1.整数 int是Sql Server主要整数类型.tinyint,smallint,int 不会自动转成bigint. 大于 2,147,483,647 的整数常量将转换为 decima ...
- Java 类执行顺序
1.如果父类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块2.如果类有静态成员赋值或者静态初始化块,执行静态成员赋值和静态初始化块3.将类的成员赋予初值(原始类型的成员的值为规定值,例 ...
- 【原】简单shell练习(三)
1.软链 linux下的软链接类似于windows下的快捷方式 # ln -s /home/gamestat /gamestat ln -s a b 中的 a 就是源文件(已经存在的文件),b是链 ...
- Quartz任务调度2
注意: 不同的版本的jar包,具体的操作不太相同,但是思路是相同的:比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联.SimpleTrigger和CornTrigge ...
- [Python3网络爬虫开发实战] 6.2-Ajax分析方法
这里还以前面的微博为例,我们知道拖动刷新的内容由Ajax加载,而且页面的URL没有变化,那么应该到哪里去查看这些Ajax请求呢? 1. 查看请求 这里还需要借助浏览器的开发者工具,下面以Chrome浏 ...
- YUM:Yellow dog Updater Modified
1. 什么是YUM YUM(全称为 Yellow dog Updater Modified) 是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器.基于RPM包管理,能够从 ...