Udacity调试课笔记之断言异常
Udacity调试课笔记之断言异常
这一单元的内容不是很多,如Zeller教授所说,就是如何写、检查断言,并如何使用工具实现自动推导出断言的条件。
现在,多数的编程语言,尤其是高级编程语言都会有内置的断言语句或断言函数。而随手编写个简易的断言也不件难事。使用内置的断言会有很多优点,比如获知出错断言的位置,可以通过编程语言的编译参数等来打开或关闭断言——即所谓的优化。
个人觉得,本单元的笔记想写成一篇博文会比较空。算起来,上一单元教授了一个方法、过程,可以让人去遵循、实践。这一单元教的断言,想得简单一点,就只是一条声明语句或函数,仅仅是语法,而且只是一条语法,那有什么可说的;从复杂角度来看,如何准确把握断言条件的选择,这又要靠个人经验的积累,也不是一门甚至一个单元的课程能讲清楚的。所以本单元会比较空洞。
言归正传,Zeller教授把断言分为两种,在测试代码中的断言可以检测某一次运行的结果;而在源代码中的断言则可以在每次运行时进行检测——所以Zeller教授强烈建议在代码,哪怕是产品代码中,都要保留断言。
断言的作用想来认识它的人心里也都清楚,不就是判断异常状态吗?Zeller教授将其细分成三点:
1、捉虫,尽早地发现错误。
2、点有什么区别。
3、文档,断言的条件、它的执行情况都可以作为文档。尤其是根据断言设置的条件,不仅是检查测试的结果,同时还可以生成测试——的用例。
而为了更好地实现断言的目标,可以给每个公共函数设置前置条件断言和后置条件断言。在函数的一开始,用前置条件检查参数性质;在函数结束之前,用后置条件检查结果。
只要函数的大小合理(一个函数1000行就什么都不用说了),那么这些公共函数的前置条件、后置条件综合起来,就会织成一张很大的网,网住程序的大部分状态,减少bug的藏身之处。如果在运行时,断言被触发,那将会离实际的bug更近,从缺陷代码到断言异常之间的程序状态就更少,就更好调试;如果断言最终一个都没被触发,也能因为断言保证了程序一些部分的正确性,而减少调试的工作量。
对于单个函数而言,前置、后置条件断言相当有用。而从整个程序的角度来看,断言也有用武之地。
程序中,有一些数据对象可能会若干个要保持一致的性质。比如像数据结构——树,就一定要无环,所以在增删结点操作之前、之后都需要检查这一性质,正如前置条件断言对参数性质的检查一样。这种始终保持一致的性质就叫数据不变量。
由于数据不变量主要用于大型、复杂的数据结构,在它们的操作类函数的开始和结尾进行性质检查,所以个人想来,数据不变量可以算作是个扩大、统一版的前置、后置条件断言。
Zeller教授举了两个例子。一个是时间炸弹——平常玩潜伏的缺陷代码(遵循Zeller教授的观点,尽量不用bug一词),但一到关键时候,就会迫不及待地蹦跶出来,把你炸个焦头烂额。所以用数据不变量来严密监控之。随便吐槽一句,《潜伏》结局余则成学母鸡这个举动也太显眼了吧?
二是复杂的红黑树。红黑树要保持一致的性质就多了,Zeller教授一下就写出了5个检查函数,略晕。
另外还存在一个系统不变量,定义是在整个执行过程中保持不变的性质。呃,这个定义相当之含糊。我想不到特别适当的例子。Zeller教授举的是 C/C++内存溢出。但是我觉得防止内存溢出不能算作是一个性质,故而认为这个例子举得不恰当。
讲了这么多有的没的,归根到底,还是只关心怎么用,有没有比较推荐的使用方式。Zeller教授推荐步骤:
1、定义数据不变量。根据数据结构的性质,确定数据不变量,检查对数据结构的操作,尽早发现错误。
2、定义前置条件。检查函数参数的性质,相对来说,比较容易找到适当的前置条件。
3、定义后置条件。检查返回结果。在较难确定的条件下,后置条件可以相对放宽松些。
4、系统不变量则是根据程序需要来定义。
而如何自动推导不变量条件,Zeller教授推荐一个工具Daikon,编程练习写个简单版的Daikon。这个工具的核心思想是通过多次运行,用运行中函数的每个参数、返回结果的值,在工具的模式库中进行匹配。匹配上的模式保留,不匹配的删掉。很多次运行并分析后,留下的模式就作为参考的不变量条件。
思想很简单,穷举加匹配,重点就是看模式库中是否存在需要的模式了。库中模式越多,找到正确不变量条件的机会就越高,结果也就是花费时间越多。个人感觉,在对代码熟悉、业务熟悉的条件下,比如深刻理解了需求后,写出的代码,自己动脑寻找的不变量条件,应该要比这个自动工具的质量高。
使用断言还有一些注意事项:
1、断言也是费时的,尤其是检查内容比较复杂时,性能影响越大。所以可以关掉。
2、断言的条件中,应该只含有结果、getter一类,而不应该有设置器(setter)等进行操作的函数或语句。
3、断言不该用于检查公共前置条件。看不明白是吧?大概就是说,一定要检查的一些内容不应该用断言,因为断言可能被关掉。
Zeller教授举的是第三方输入,我不太熟悉。我就改举java里的IO,或是所有语言里打开文件的操作。为防止打开文件失败,一定要用if 或 try ... Catch语句来检查打开状态,否则一旦断言被关掉,这个后果……反而会出错。
最后就是来讨论下,为什么要用断言异常了。C/C++和Python里虽然断言默认是打开、支持的,但我很少用过。而Java里更是默认不使用断言异常。就像上面说的,可以用if或try...catch 来代替断言。断言可有两个缺点,一是影响性能,二是对用户不友好,你看,一旦触发断言,程序90%跟你说不玩了,这太不友好了。
先列Zeller教授举的断言三优点:
1、程序及时退出,哪怕只是小错误而退出,也比坏数据好,因为坏数据不知道会造成什么样的后果。很多人说的史上最昂贵的bug,阿丽亚纳五号运载火箭,就因为惯性制导系统无法将一个64位的数据转换到16位格式而烧掉了5亿美元。
Zeller教授说,本来开发时是有对格式转换进行检查的断言的,结果产品中因为考虑性能问题,能关掉了这些断言。另外即使断言被触发,这火箭的软件系统也有足够优秀的异常处理机制来继续工作。所以可能就因为节省了这么一些断言,而创造了世上最奢侈的烟花。和火箭、航天飞机放的烟花比起来,北京奥运的烟花花费确实算不上多……
2、断言可以让调试更简单。理由之前说过,更早地发现错误,更少的bug位置。
3、方便回溯缺陷。从出错断言开始,往回找,和2差不多的。
不过,Zeller教授也承认可以关闭一些影响性能的断言。拿红黑树来说,没必要在一次时间复杂度O(log n) 的插入操作的前面,分别进行时间复杂度为O(n)的遍历操作,就为了检查是否存在环这一性质。这样的断言没必要。
再来说说我的理解。首先是为什么if和try...catch不能代替断言,也就是为什么还要用断言,这么一个不友好的语句。
个人理解,因为一个最简单的断言函数大概就是,如果断言条件为假,则抛出异常。其实也就是一个if语句能完成的事。但是一个简单的if语句抛出的异常所能显示的信息,不如标准的断言异常的信息多。而一个assert(condition)在很多时候从便捷上、作用上都高于一个if (condition) throw XXX; 所以If 不能代替断言。
而try...catch的情况相似,除了像IO等操作可能已经有内置的异常机制,面对新的异常条件时,同样要用if(condition)throw 异常,或assert(condition)来抛出异常,方能catch到,用if的话,也就可能需要定义新的异常类型,更加不如断言来得方便。当然try...catch和断言功能上重叠得不多,更加没有取代的可能了。
try...catch更像是断言的母亲,在断言捣蛋、对用户不好的时候,她来揪着断言的小耳朵,给用户赔礼道歉。
这一单元就这么简单:
会用断言
勤用断言
善用断言
Udacity调试课笔记之断言异常的更多相关文章
- 菜农群课笔记之ICP与ISP----20110412(整理版)
耗时一上午时间对HOT大叔昨晚的群课内容进行温故并整理,现将其上传,若想看直播可到下面链接处下载:http://bbs.21ic.com/icview-229746-1-1.html 成 ...
- udacity android 学习笔记: lesson 4 part b
udacity android 学习笔记: lesson 4 part b 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 ...
- udacity android 实践笔记: lesson 4 part b
udacity android 实践笔记: lesson 4 part b 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 ...
- udacity android 学习笔记: lesson 4 part a
udacity android 学习笔记: lesson 4 part a 作者:干货店打杂的 /titer1 /Archimedes 出处:https://code.csdn.net/titer1 ...
- Elasticsearch7.X 入门学习第一课笔记----基本概念
原文:Elasticsearch7.X 入门学习第一课笔记----基本概念 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...
- 清华大学ucore操作系统课笔记
操作系统 清华大学ucore操作系统课笔记 全文思维导图 1. 操作系统概述 1.1 什么是操作系统? 操作系统的定义 没有公认的精确定义 一个控制程序 一个系统软件 控制程序执行过程,防止错误和计算 ...
- OllyICE 调试的程序无法处理异常 解决方法
问题描述 在用OllyICE打开可执行文件时出现如下图所示错误 解决方法 1. 选项 -> 调试设置 , 打开调试选项 2. 切换到 异常 页签 3. 取消勾选 忽略(传递给程序)以下异常: 单 ...
- OD调试学习笔记7—去除未注册版软件的使用次数限制
OD调试学习笔记7—去除未注册版软件的使用次数限制 本节使用的软件链接 (想自己试验下的可以下载) 一:破解的思路 仔细观察一个程序,我们会发现,无论在怎么加密,无论加密哪里,这个程序加密的目的就是需 ...
- w2wp.exe 已附加有调试器,但没有将该调试器配置为调试此未经处理的异常
一.问题描述 昨天系统联调,用到了VS2010 附件进程,把w2wp.exe 进程添加到vs2010 的调试进程中,这样其他系统访问我们系统,就可以捕获断点进行调试 但是,今天F5 调试的时候,发现直 ...
随机推荐
- maven 打包源文件
1.The source plugin can be used to create a jar file of the project sources from the command line or ...
- iOS中如何获取image.xcassets中的启动图片
/** * 获取启动图片 */ +(UIImage *)launchImage{ NSString *imageName=@"LaunchImage-700"; if(iphon ...
- 【转载】CocoaPods安装和使用教程
转自:http://code4app.com/article/cocoapods-install-usage 目录 CocoaPods是什么? 如何下载和安装CocoaPods? 如何使用CocoaP ...
- Impala 源码分析-FE
By yhluo 2015年7月29日 Impala 3 Comments Impala 源代码目录结构 SQL 解析 Impala 的 SQL 解析与执行计划生成部分是由 impala-fronte ...
- Entity Framework - Func引起的数据库全表查询
原文:http://www.cnblogs.com/dudu/archive/2012/04/01/enitity_framework_func.html 使用 Entity Framework 最要 ...
- untiy绘制网格mesh
关于绘制网格, 雨松前辈 已经解释的非常的到位,这里我只是搬运工,实在是感觉自己去描述的话不会有雨松大神描述的清楚,该文章循序渐进,一步步引导读者去理解unirty 绘图机制,真的是没有比这个再好得了 ...
- C++类中的静态成员变量与静态成员函数
最近一直看c++相关的项目,但总是会被c++类中的静态成员变量与静态成员函数的理解感觉很是模糊,不明白为什么类中要是用静态成员变量.于是在网上搜集了一些资料,自己再稍微总结下. 静态成员的概念: 静态 ...
- JSP学习笔记 - 源码 -- JSP Custom Tags -- JSP自定义标记
NetBeans 项目demo下载地址>> http://files.cnblogs.com/files/AndrewXu/JSPCustomTags.zip
- cobbler之ks文件示例
#platform=x86, AMD64, or Intel EM64T #version=DEVEL # Firewall configuration,关闭防火墙 firewall --disabl ...
- 添加AdMob 错误记录
依照官方教程添加文件及其 frameWork后 发现运行报错 错误如下 Undefined symbols for architecture i386: "_OBJC_CLASS_$_ASI ...