编程精粹—— Microsoft 编写优质无错 C 程序秘诀 01:假想的编译器

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

When I set out to write the first edition of Writing Solid Code twenty years ago, I had a simple thought in mind: Give programmers proven tools, techniques, and philosophies to help them write rock-solid, bug-free code.

二十年前,当我开始编写《Writing Solid Code》第一版时,我心中有一个简单想法:给程序员们提供经过验证的工具、技术和开发哲学,帮助他们编写出坚如磐石、零错误的代码。

Over the years, programmers have regularly asked me if my views have changed much since the book’s original publication.

多年来,程序员们经常问我,自从该书首次出版以来,我的观点是否发生了很大变化。

Not only have my views not changed over all those years, but I’ve embraced the concepts and philosophies expressed in the book’s pages even more staunchly.

这么多年来,我的观点不仅没有改变,反而更加坚定地支持书中阐述的概念和开发哲学.

I had no idea that the book would become a runaway best-seller, eventually being translated into more than 16 different languages. I certainly didn’t expect that so many software development companies would make the book required reading for their developers. Nor did I anticipate that Universities around the world would use the book in their computer science courses.

我完全没有想到这本书会成为畅销书,最终还被翻译成超过16种不同的语言。我当然也没有预料到会有这么多软件开发公司将这本书列为他们的开发人员的必读书目。我也没有预料到全球各地的大学会在他们的计算机科学课程中采用这本书。

作者 Steve Maguire 曾在微软担任高级项目经理和软件工程师。这是作者的第一本书,它的后续之作是《Debugging the Development Process》,中译版名字为《微软研发:致胜策略》,我会在将来介绍这本书。在这两本书中,他分享了大量关于他在微软工作期间的经验和见解。


编写无错误代码的关键是要更加了解 错误 是如何产生的。多年以来,作者经常问自己两个关键的问题,从这两个问题中得到的答案,形成了本书介绍的诀窍。程序员遇到的每一个错误,都要问自己这两个关键的问题:

  1. 怎样才能自动地查出这个错误?

    How could I have automatically detected this bug?

  2. 怎样才能避免这个错误?

    How could I have prevented this bug?

本书提供的指南在大多数情况下你都应该遵循,但当你打破它们时可以获得更好的结果时,那么就打破它

要记住:

在任何时候,跟在大多数人的后面常常是所能选择的最坏道路。因此在成为别人的追随者之前一定要确定这样做确实有意义,而不是仅仅因为其他人如此自己也亦步亦趋。


不记录,等于没读。本文记录书中第一章内容:假想的编译器。


假想的编译器

如果存在一个理想的编译器,它能检测到程序中的每个错误并给出错误信息,那么消除代码中的错误将会非常简单。问题是,这种无所不能的编译器并不存在,但是我们可以有一些方法,可以自动检测到更多的错误:

  • 启用所有可选的编译器警告
  • 使用语法和可移植性检查工具(比如 CppcheckPC-LIntSplint 等 )
  • 使用自动化单元测试

先考察一下测试人员是如何发现 BUG 的:

  1. 向程序输入数据,然后观察输出。
  2. 如果他注意到一些数字是错误的,或者某个功能没达到预期,或者程序崩溃了,那么就发现了一个BUG。

如果他输入的数据恰好无法触发 BUG 呢?如果他一时疏忽错过异常现象呢?那么这个 BUG 就会溜到正式版本中,然后在将来的某个时候被用户遇到。

所以说,测试人员发现 BUG 是靠运气吗

是的。

这并不是批判测试人员的所做所为的,这只是想指出一个事实:很难用黑盒测试一个程序。利用黑盒测试能做的只是往程序里填数据,并看它输出什么。这就好比确定一个人是不是疯子一样:你问一些问题,你倾听对方回答,然后你做出判断。但这样你还是不能确定这人是不是疯子,因为你不知道对方脑袋里在想些什么。

你永远会质疑黑盒测试的输入数据

  • 数据够吗?
  • 数据对吗?

不要依赖黑盒测试。去做一些事情,抓住每一个机会 自动地 捕获BUG,而不是靠运气。这些事情包括:

  • 启用所有可选的编译器警告
    好的编译器能够把屡次出错的合法 C 习惯用法看成是拼写错误。比如某些编译器可以将警告级别设置为 MISRA C 2004 或者 MISRA C 2012,而 MISRA C 是一套用于嵌入式系统中编写高可靠性和高安全性软件的编码准则,最初是为汽车行业制定。它实际上是标准 C 的一个子集,对标准 C 附加了一系列规则和指导。使用 MISRA C 会更严格的检测代码,有些标准 C 允许的用法会被警告。本文附录 1 给出一些这样的例子。
  • 使用语法和可移植性检查工具(比如 CppcheckPC-LintSplint 等 )
    通常这些检查器检查的错误更详细、更彻底。一旦原程序变成了没有 PC-Lint 错误的形式,继续保持就变得很简单了。
  • 如果有单元测试,请使用它们
    不要过高估计自己编写正确代码的能力,要做测试!即使没有新增代码,只是调整代码;或者做很小的修改都要运行单元测试。
    有时,似乎可以跳过一些安全措施,这些安全措施用来避免程序出错。但走捷径之时,就是麻烦将近之日

消除代码中 BUG 的最好方法是找到它们,越早越好。寻找自动捕获 BUG 的方法。

努力减少程序员迫不得已的排错,优先让编译器和工具(比如 PC-Lint)指明错误。

附录 1

1 while 循环错放了一个分号

如下代码给出了一个复制内存代码,但 while 表达式后误写了一个分号。

void* memcpy(void *pvTo, void *pvFrom, size_t size) {byte *pbTo = (byte *)pvTo; byte *pbFrom = (byte *)pvFrom; while(size-->0); 					//<--- 这里错误的键入了分号*pbTo++ = *pbFrom++; return(pvTo); 
}

对于这个代码,逻辑上是错误的,但编译器认为这是一个完全合法的 while 语句,其循环体为空语句。由于使用空语句的场景比较少,所以编译器常常在遇到循环体为空语句时给出一条可选的警告信息,如果你选择启用这个警告信息,编译器就会自动提醒你注意这样的错误。如果你真正要使用循环体为空语句的时候,可以使用编译手册建议的解决方法,比如使用一个将被优化掉的常量表达式 ( NULL ),或者使用一个空块 ({ })。这里使用空块举例:

char *strcpy(char *pchTo, char *pchFrom) {char *pchStart = pchTo;while(*pchTo++ = *pchFrom++){}								//<--- 这里使用了空块return (pchStart);
}

2 错误的赋值

C 语言允许在编写表达式的任何地方使用赋值 (=) ,但如果不小心,这种额外的灵活性可能带来错误。比如:

if(ch = '\t')							//<--- 本想与制表符比较,但实际是赋值ExpandTab();

这种代码编译器不会产生错误,因为代码是合法的。现代的编译器一般能检查到这种可能的错误,并给出警告信息。除了 if 控制表达式外,还有:

  • forwhile 控制表达式
  • &&|| 表达式

在以上表达式中,如果确实需要使用赋值,比如上面例子的拷贝代码:

while(*pchTo++ = *pchFrom++)			//<--- 这里的赋值可能会产生编译警告{}

为了不让编译器产生警告信息,可以改写成:

while(()*pchTo++ = *pchFrom++) != '\0'){}






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

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

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

相关文章

MyBatis拦截器(Interceptor)的理解与实践

文章目录 1. 什么是MyBatis拦截器&#xff1f;2. 拦截器的基本原理3. 编写自定义拦截器3.1 示例&#xff1a;实现SQL执行时间统计拦截器3.2 配置拦截器 4. 实战应用场景5. 总结 &#x1f389;欢迎来到SpringBoot框架学习专栏~ ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨博…

原生js制作svg 图标生成动态 tab栏切换效果(结尾附代码)

svg 图标生成动态 tab 栏 先看效果&#xff1a; 我想做一个 tab 栏比较美观的效果&#xff0c;当然切换的数据可以自己做一下&#xff0c;这里不演示&#xff0c;说一下特效如何制作。 当我点击时要将空心变为实心的这么一个效果&#xff0c;所以准备两个五角星样式一个是空…

Java面试八股之myBatis的优缺点

myBatis的优缺点 优点&#xff1a; 灵活性高&#xff1a; MyBatis允许直接编写原生SQL语句&#xff0c;这意味着你可以针对特定的数据库特性进行优化&#xff0c;处理复杂的查询逻辑&#xff0c;从而更好地满足业务需求。 易于上手&#xff1a; 相比Hibernate等其他ORM工具&…

深度学习算法面经(高频核心问题总结,持续更新)

学习的过程短期目标是丰富己身&#xff0c;长远来看有的人为了就业财富自由&#xff1b;有的则为了创造一些有意义的事物&#xff0c;更多的是为了前者。 此文章用于记录和总结深度学习相关算法岗的各种面试问题&#xff0c;搜集答案并加入博主一些浅显的理解,欢迎评论区纠正、…

第6章 设备驱动程序(4)

目录 6.5 块设备操作 6.5.5 请求结构 6.5.6 BIO 6.5.7 提交请求 6.5.8 I/O调度 6.5.9 ioctl实现 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;查看后续文章。 6.5 块设备操作 6.5.5 请求结构 struct request { //放在请求队列上&#xff0…

curl发送邮件需要哪些参数设置?如何配置?

curl发送邮件有哪些认证方式&#xff1f;如何通过curl命令发信&#xff1f; curl是一个命令行工具&#xff0c;用于在网络上传输数据&#xff0c;包括发送电子邮件。要使用curl发送邮件&#xff0c;需要设置一些参数以确保邮件被正确发送到目标收件人。AokSend来介绍一些必需的…

【Unity】Animator动画倒播,与StartRecording动画录制

一、Animator动画倒播 正常我们修改速度&#xff0c;只需要修改Animator.speed即可&#xff0c;但如果设置为负值&#xff0c;Animator系统会自动将其改为0值。 1.创建动画速度参数 (1)设置动画 我们需要创建表示速度的动画参数Speed&#xff0c;将其付给需要倒播的动画片段…

改进位删除谜题的求解方法

问题背景 给定长度为 n 的二进制向量&#xff0c;如何删除恰好 n/3 个位&#xff0c;使剩余二进制向量的不同数量最小化。该问题被称为“位删除谜题”。 以下是该问题的示例&#xff1a; 对于 n 3 的情况&#xff0c;最优解是 2&#xff0c;对应两个不同的向量 11 和 00。对…

韩国裸机云站群服务器托管租用方案

随着网络技术的飞速发展&#xff0c;站群服务器在网站运营中扮演着越来越重要的角色。韩国裸机云站群服务器&#xff0c;以其独特的优势&#xff0c;如地理位置优越、价格相对较低、技术实力雄厚等&#xff0c;吸引了众多企业的关注。本文将为您详细介绍韩国裸机云站群服务器的…

如何快速翻译pdf英文论文(5分钟就可以翻译一篇几十页的英文论文)

一、问题&#xff1a;如何快速翻译pdf英文论文 二、解决方法&#xff1a; 可以通过下面三个在线翻译来进行翻译pdf文档 百度翻译有道翻译谷歌翻译 方法&#xff1a;以有道翻译为例&#xff0c;可以直接百度搜索有道在线翻译&#xff0c;然后点击文档翻译&#xff0c;将pdf文…

Python抓取天气信息

Python的详细学习还是需要些时间的。如果有其他语言经验的&#xff0c;可以暂时跟着我来写一个简单的例子。 2024年最新python教程全套&#xff0c;学完即可进大厂&#xff01;&#xff08;附全套视频 下载&#xff09; (qq.com) 我们计划抓取的数据&#xff1a;杭州的天气信息…

docker pull xxx拉取超时time out

文章目录 前言总结 前言 换了镜像源&#xff0c;改配置的都不行&#xff0c;弄了一个下午&#xff0c;最后运行一下最高指令就可以了 sudo docker_OPTS"--dns 8.8.8.8"总结 作者&#xff1a;加辣椒了吗&#xff1f; 简介&#xff1a;憨批大学生一枚&#xff0c;喜欢…

02-ES6新语法

1. ES6 Proxy与Reflect 1.1 概述 Proxy 与 Reflect 是 ES6 为了操作对象引入的 API 。 Proxy 可以对目标对象的读取、函数调用等操作进行拦截&#xff0c;然后进行操作处理。它不直接操作对象&#xff0c;而是像代理模式&#xff0c;通过对象的代理对象进行操作&#xff0c;…

WEB3-众筹合约

构建众筹合约的框架&#xff0c;包括定义 IERC20 接口&#xff0c;并通过构造函数初始化代币合约。 By:HackQuesthttps://www.hackquest.io/zh

如何用Vue3和ApexCharts打造引人注目的3D径向条形图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 ApexCharts 构建美观的 Vue.js 径向条形图 应用场景 径向条形图是一种用于可视化单一数据点及其与目标或理想值的关系的图表类型。它在显示进度、完成率或其他类似度量时非常有用。 基本功能 这段代码…

同三维T80002JEHV H.265高清解码器

同三维T80002JEHV H.265高清解码器 1路HDMI1路VGA解码输出&#xff0c;1/2/4画面分割或16路轮询显示 产品简介&#xff1a; 同三维T80002JEHV解码器使用Linux系统&#xff0c;支持VGA/HDMI二种接口同时输出&#xff0c;支持多流输入多流解码及多屏显示&#xff0c;具有完善的…

Django REST framework数据展示技巧:分页、过滤与搜索的实用配置与实践

系列文章目录 Django入门全攻略&#xff1a;从零搭建你的第一个Web项目Django ORM入门指南&#xff1a;从概念到实践&#xff0c;掌握模型创建、迁移与视图操作Django ORM实战&#xff1a;模型字段与元选项配置&#xff0c;以及链式过滤与QF查询详解Django ORM深度游&#xff1…

【修复Win11错误 0x80010135: 路径太长】

1. 问题现象&#xff1a; 一个意外错误使你无法复制该文件。如果你继续收到此错误&#xff0c;可以使用错误代码来搜索有关此问题的帮助。 错误 0x80010135: 路径太长 或者这样 2. 分析问题 造成这个问题的主要原因包括&#xff1a; 文件路径长度超过 260 个字符&#xf…

Qt利用Coin3D(OpenInventor)进行3d绘图

文章目录 1.安装1.1.下载coin3d1.2.下载quarter1.3.解压并合并 2.在Qt中使用3.画个网格4.加载wrl模型 1.安装 1.1.下载coin3d 首先&#xff0c;到官网下载[coin3d/coin] 我是Qt5.15.2vs2019的&#xff0c;因此我选择这个coin-4.0.2-msvc17-x64.zip 1.2.下载quarter 到官网…