编程精粹—— Microsoft 编写优质无错 C 程序秘诀 02:设计并使用断言

这是一本老书,作者 Steve Maguire 在微软工作期间写了这本书,英文版于 1993 年发布。2013 年推出了 20 周年纪念第二版。我们看到的标题是中译版名字,英文版的名字是《Writing Clean Code ─── Microsoft’s Techniques for Developing》,这本书主要讨论如何编写健壮、高质量的代码。作者在书中分享了许多实际编程的技巧和经验,旨在帮助开发人员避免常见的编程错误,提高代码的可靠性和可维护性。


不记录,等于没读。本文记录书中第二章内容:设计并使用断言。


一个好的开发策略是维护程序的两个版本:发布版本调试版本。通过在调试版本中使用 断言 语句,你可以检测以下错误:

  • 错误的函数参数
  • 未定义行为
  • 其他程序员做出的错误假设
  • 不知何故出现的不可能条件

仅在调试模式下启用的备用算法有助于验证函数结果和函数中所使用算法的正确性。

第一章介绍了一些方法,以便检测到更多的错误,比如启用所有可选的编译器警告、使用使用语法和可移植性检查工具等。这些措施当然是好的,这是这些措施只能查出所有错误的一小部分。举一个例子:

strCopy = memcpy(malloc(length), str, length);

这句代码在多数情况下都会工作良好,直到 malloc 函数调用失败。当 malloc 失败时,就会给 memcpy 函数返回 NULL 指针,从而出错。

编译器检查不出这种错误,同样,编译器也检查不出算法错误,无法验证程序员所作的假设。寻找这种错误非常艰苦,但如果在实现 memcpy 函数时,检查一下参数是否有效,就可以自动捕获这个错误。

聪明的程序员将调试代码隐藏在断言 assert。断言的好处是用户在错误发生时,可以自动地把它们检查出来。

assert 是一个宏,如果其参数的计算结果为假,就中止调用程序的执行。

assert不应该产生其它副作用。因此assert应该为宏而不是函数,因为函数调用会引起不期望的内存或代码交换。

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

比如前面提到的 memcpy 函数,我们可以使用断言进行参数确认,代码如下:

void memcpy(void* pvTo, void* pvFrom, size_t size) {void* pbTo = (byte*)pvTo;void* pbFrom = (byte*)pvFrom;assert(pvTo != NULL && pvFrom != NULL);		//<--- 使用断言while(size-->0)*pbTo++ == *pbFrom++;return(pvTo);
}

这样,当给参数 pvTopvFrom 传递 NULL 时,就会触发断言,从而自动的发现程序代码错误。

使用断言避免未定义行为

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

使用库函数memcpy时,如果在存储空间相互重叠的对象之间进行了拷贝,结果是未定义的。

可以使用断言来进行重叠检查:

ASSERT(pbTo >= pbFrom + size || pbFrom >= pbTo + size);

假如你之前从来没有见过重叠检查,当这个断言捕获到错误时,你能看出来它是什么意思吗?

断言捕获了一个错误,我们却不知道断言的作用,没有什么比这件事更令人沮丧了。

如果搞不清楚相应断言检查的是什么,就很难知道错误是出现在程序中,还是出现在断言中。

解决这个问题的方法是:给不够清晰的断言加上注解

不要浪费别人的时间——详细说明不清晰的断言

一个人在穿过森林时,看到树上钉着一块大牌子,上面写着两个红色大字:“危险”。但危险到底是什么?

除非告诉人们危险是什么或者危险非常明显,否则这个牌子起不到提高人们警觉的作用,人们会忽视牌子上的警告。

同样,程序员不理解的断言也会被忽视。

利用断言消除隐式假设

如果假设long占用4个字节,可以使用以下断言来检查假设是否正确:

ASSERT(sizeof(long) == 4);

在编写函数时,要进行反复的思考,并且自问:“我打算做哪些假设?”

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

另外,还要问:“这个程序中最可能出错的是什么,怎样才能自动地查出相应的错误?”努力编写出能够尽早查出错误的测试程序。

利用断言检查不可能发生的情况

断言是检查那些在正常情况下绝不应该发生的情况,而不是用于检查错误。换句话说,断言是防止程序员失误的一种手段,绝不要把必须执行的代码放入断言中

比如函数参数不可能超过某个范围,可使用断言来验证;malloc 函数返回是否为 NULL 就不能使用断言来检查,判断是否为 NULL 是错误检查的一部分;

比如一个解压缩协议规定:如果遇到0xA5,那么0xA5后面必须是 字符压缩序列,字符压缩序列占用两个字节,第一个字节表示 压缩的字符,第二个字节表示 字符重复的个数

我们认为 字符序列 只能有两种情况:

  1. 压缩的字符是 0xA5,字符重复个数固定为1
  2. 压缩的字符不是 0xA5,字符重复个数必须大于等于4

则可以使用以下断言对规则进行验证:

#define REPEAT_CODE    0xA5
//...
ASSERT( size>=4 || (size==1 && b==bRepeatCode) )

如果这一断言失败说明内容不对或者字符压缩程序中有错误。无论哪种情况都是错误,而且是不用断言就很难发现的错误。

利用断言发现静默的异常行为

防错性程序设计常常被誉为有较好的编码风格,但它却隐瞒了绝不应该发生的错误。

核反应堆堆芯过热,程序自动地向堆芯灌水、插入冷却棒或者做其它能让堆芯冷却下来的方法。只要程序已经控制了事态,就不会向有关人员报警。

这和我们写的防错程序很像,当某些意料不到的事情发生时,程序只进行静默处理。

但是堆芯不会无缘无故地出现过热现象,一定是发生了某种不同寻常的事情,才会引起这一故障。但在值班人员眼里,核反应堆一切正常,错误被隐瞒了。

即便如此,防错性程序仍然有它的价值。我们希望在进行防错性程序设计时,不要隐瞒错误

一方面一如既往地利用防错性程序设计进行编码,另一方面在事情变糟的情况下利用断言进行报警

核反应堆堆芯过热,立即向工作人员发出警报。同时,程序自动地向堆芯灌水、插入冷却棒或者做其它能让堆芯冷却下来的方法。

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


有很多书都有讲解断言,有很多开源代码都在使用断言。在工作了几年之后,我对断言也有了一些见解,我将它们写在了《随想012:断言》 这篇文章中,推荐阅读。






每一份打赏,都是对创作者劳动的肯定与回报。
千金难买知识,但可以买好多奶粉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/854765.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JeecgFlow排他网关演示

排他网关概念理解 排他网关&#xff0c;也称为异或(XOR)网关&#xff0c;用于流程中实现分支决策建模。排他网关需要搭配条件顺序流使用。 当流程流转到排他网关时&#xff0c;所有流程顺序流都是会顺序求解&#xff0c; 其中第一条条件为true的顺序流会被选中(当有多条顺序流都…

澳汰尔(Altair)3D 打印部件设计仿真——打造高效的增材制造设计

借助 Inspire Print3D&#xff0c;可加速创新、结构高效的 3D 打印部件的创建、优化和研究&#xff0c;提供快速准确的工具集&#xff0c;可用于实现选择性激光熔融 (SLM) 部件的设计和过程仿真。 工程师可以快速了解影响可制造性的工艺或设计变更&#xff0c;然后将部件和支撑…

SoC设计更重要的是IP管理

对于大多数片上系统&#xff08;SoC&#xff09;设计来说&#xff0c;最关键的任务不是RTL编码&#xff0c;甚至不是创建芯片架构。今天&#xff0c;SoC的设计主要使用来自多个供应商的各种IP块。这使得管理硅IP成为SoC设计过程中的主要任务。 一般来说&#xff0c;新编写的RTL…

Swift Combine — JUST Publisher

之前文章介绍的Publisher都是可以连续发送数据的&#xff0c;Subscriber也可以一直接收数据&#xff0c;除非收到了finished或者error而结束。而JUST Publisher则不同&#xff0c;它只向每个订阅者发送一次输出&#xff0c;然后结束。 一起来看一下下面的代码。 class JustVi…

从0到1:手动测试迈向自动化——手机web应用的自动化测试工具

引言&#xff1a; 在当今移动互联网时代&#xff0c;手机web应用已经成为人们生活中不可或缺的一部分。为了保证手机web应用的质量和稳定性&#xff0c;自动化测试工具变得十分重要。本文将介绍手机web应用自动化测试工具的选择和使用&#xff0c;提供一份超详细且规范的指南&a…

GPT3.5的PPO目标函数怎么来的:From PPO to PPO-ptx

给定当前优化的大模型 π \pi π&#xff0c;以及SFT模型 π S F T \pi_{SFT} πSFT​ 原始优化目标为: max ⁡ E ( s , a ) ∼ R L [ π ( s , a ) π S F T ( s , a ) A π S F T ( s , a ) ] \max E_{(s,a)\sim RL}[\frac{\pi(s,a)}{\pi_{SFT}(s,a)}A^{\pi_{SFT}}(s,a)] m…

重构大学数学基础_week05_雅各比矩阵与雅各比行列式

这周来讲一下雅各比矩阵和雅各比行列式。 多元函数的局部线性属性 首先我们来回顾一下向量函数&#xff0c;就是我们输入一个向量&#xff0c;输出也是一个向量&#xff0c;我们假设现在有一个向量函数 这个函数意思就是在说&#xff0c;我们在原来的平面上有一个向量(x,y),经…

美团Meitu前端一面,期望27K

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 1、做的主要是什么项目&#xff0c;桌面端的吗&#xff1f; 2、用的主要是什么技术栈&#xff1f;vue有了解吗&#xff1f; 3、移动端开发一般怎么…

使用Ventoy制作U盘启动安装系统

简介 Ventoy是一个制作可启动U盘的开源工具。 无需反复地格式化U盘。你只要制作一次U盘启动盘&#xff0c;后面你只需要把 ISO/WIM/IMG/VHD(x)/EFI 等类型的系统镜像文件直接拷贝到U盘里面就可以启动了&#xff0c;无需其他操作。可以一次性拷贝很多个不同类型的镜像文件&…

vue+element-plus完美实现跨境电商商城网站

目录 一、项目介绍 二、项目截图 1.项目结构图 2.首页 3.中英文样式切换 4.金钱类型切换 5.商品详情 6.购物车 7.登录 ​编辑 8.注册 9.个人中心 三、源码实现 1.项目依赖package.json 2.项目启动 3.购物车页面 四、总结 一、项目介绍 本项目在线预览&am…

提拔你,还是干掉你,从来不是看技术

有读者问我&#xff0c;技术人员工作5~10年就逐渐拉开了差距&#xff0c;这背后的原因是什么&#xff1f;思考片刻后&#xff0c;我回答&#xff1a;是底层能力。 K哥有20年职场经验&#xff0c;从程序员到技术高管一路走来&#xff0c;我总结了技术人员最重要的一些认知和底层…

如何通过小猪APP分发轻松实现Web封装APP

你有没有想过将你的网站或者Web应用变成一个真正的APP&#xff1f;这听起来可能有点复杂&#xff0c;但其实在今天的技术环境下&#xff0c;这已经变得非常简单了。特别是有了像小猪APP分发这样的工具&#xff0c;你可以轻松地将你的Web应用封装成一个APP。 为什么要将Web应用封…

【大数据·hadoop】项目实践:IDEA实现WordCount词频统计项目

一、环境准备 1.1&#xff1a;在ubuntu上安装idea 我们知道&#xff0c;在hdfs分布式系统中&#xff0c;MapReduce这部分程序是需要用户自己开发&#xff0c;我们在ubuntu上安装idea也是为了开发wordcount所需的Map和Reduce程序&#xff0c;最后打包&#xff0c;上传到hdfs上…

ASM-MehotdVisitor实践

使用ASM几乎用户全部的精力都是对MethodVisitor的处理&#xff0c;方法code的处理都需要使用这个类进行操作。还是之前文章说过的&#xff0c;ASM单独学习意义并不大&#xff0c;难以达到触类旁通&#xff0c;先行掌握字节码基础后再玩起ASM才能体会真正的乐趣&#xff0c;不然…

【Kubernetes】k8s 自动伸缩机制—— HPA 部署

一、在K8s中扩缩容分为两种&#xff1a; ●Node层面&#xff1a;对K8s物理节点扩容和缩容&#xff0c;根据业务规模实现物理节点自动扩缩容 ●Pod层面&#xff1a;我们一般会使用Deployment中的Replicas参数&#xff0c;设置多个副本集来保证服务的高可用&#xff0c;但是这是…

前端菜鸡流水账日记 -- git管理工具(多版本)

哈喽哇&#xff0c;我又又又来了&#xff0c;其实之前就挺想进行一篇关于git管理工具的分享的&#xff0c;但是一直都没有来的及&#xff0c;直到今天&#xff0c;在学习的时候&#xff0c;&#xff0c;一个朋友新发现了一个vscode中的小插件&#xff0c;所以我就决定一起来分享…

论文阅读ReLU-KAN和Wav-KAN

这是我读KAN系列论文的第三篇&#xff0c;今天把两篇论文放在一起写&#xff0c;分别是&#xff1a; ReLU-KAN&#xff1a; https://arxiv.org/abs/2406.02075 Wav-KAN&#xff1a; https://arxiv.org/abs/2405.12832 之所以放在一起&#xff0c;是因为这两篇论文针对KAN的…

据说可以防静电和浪涌的P6KE30CA

公司有些变送器之前在最后一道校准时&#xff0c;经常发生烧毁的情况。所以在电路的防反接的M7二极管前面又增加了一个TVS二极管&#xff0c;型号P6KE30CA。但愿加了这个好使把。今天又研究了一下这个TVS管子&#xff0c;把搜索到的东西记录一下。放这里备忘把&#xff0c;忘记…

18.实战 LLaMA2-7B 指令微调

实战 LLaMA2-7B 指令微调 实战 LLaMA2-7B 指令微调大模型训练技术总结以模型训练阶段分类&#xff1a;Pre-Training vs Fine-Tuning以微调权重比例分类&#xff1a;FFT vs PEFT以模型训练方法分类&#xff1a;Fine-Tuning vs Instruction-Tuning以模型训练机制分类&#xff1a;…

【五】Linux软件仓库Yum源--SSH远程控制--SCP远程传输

RPM&#xff08;红帽软件包管理器&#xff09; RPM建立统一的数据库文件&#xff0c;记录软件信息并分析依赖关系。目前RPM的优势已经被公众所认可&#xff0c;使用范围也已不局限在红帽系统中了。常见RPM命令如下&#xff1a; 安装软件 rpm -ivh file…