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

news2024/10/6 2:20:15

这是一本老书,作者 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.coloradmin.cn/o/1839784.html

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

相关文章

电脑提示d3dcompiler_47.dll丢失的解决方法,实测靠谱的5种方法

在计算机使用过程中&#xff0c;缺失d3dcompiler_47.dll这一系统文件是一个常见问题&#xff0c;尤其是对于游戏和图形密集型应用程序用户来说尤为重要。这个文件是DirectX软件工具包的一部分&#xff0c;主要用于处理图形渲染的应用程序接口的核心元素。当你在运行游戏或某些软…

[面试题]消息队列

[面试题]Java【基础】[面试题]Java【虚拟机】[面试题]Java【并发】[面试题]Java【集合】[面试题]MySQL[面试题]Maven[面试题]Spring Boot[面试题]Spring Cloud[面试题]Spring MVC[面试题]Spring[面试题]MyBatis[面试题]Nginx[面试题]缓存[面试题]Redis[面试题]消息队列 什么是…

什么是进程

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 在了解进程之前&#xff0c;我们需要知道多任务的概念。多任务&#xff0c;顾名思义&#xff0c;就是指操作系统能够执行多个任务。例如&#xff0c;…

微服务必备容器化技术

文章目录 docker介绍与安装及上手应用什么是容器化技术&#xff1f;为什么需要学习docker&#xff1f;如何理解dockerdocker下载与安装docker的基础组成docker体验 dockerfile介绍并创建go-zero环境容器docker的基础组成从容器构建属于go环境的容器基于dockerfile构建go容器镜像…

嵌入式linux系统中SPI子系统验证03

今天主要给大家分享一下&#xff0c;如何使用SPI总线进行验证的方法。 第一&#xff1a;SPI验证流程 1. echo 1 > /dev / spidev3.0 2&#xff0e;逻辑分析仪抓波形 3.十六进指转化为十进制 4.ASCII字符代码表匹配 第二&#xff1a;SPI验证结果 第三&#xff1a;设备…

蚓链数字化生态平台,开启企业未来新篇章!

在如今数字化浪潮势不可挡的时代&#xff0c;企业发展可谓是机遇与挑战并存&#xff01;而蚓链数字化生态平台系统的出现&#xff0c;绝非是给企业一套平平无奇的营销方案或工具那么简单。 它赋予企业的&#xff0c;是在产业生态链中获取海量数据价值的关键且强大的能力&#x…

18个机器学习核心算法模型总结

最强总结&#xff01;18个机器学习核心算法模型&#xff01;&#xff01; 大家好~ 在学习机器学习之后&#xff0c;你认为最重要的算法模型有哪些&#xff1f; 今儿的内容涉及到~ 线性回归逻辑回归决策树支持向量机朴素贝叶斯K近邻算法聚类算法神经网络集成方法降维算法主成…

Weevil-Optimizer象鼻虫优化算法的matlab仿真实现

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 Weevil-Optimizer象鼻虫优化算法的matlab仿真实现&#xff0c;仿真输出算法的优化收敛曲线&#xff0c;对比不同的适应度函数。 2.测试软件版本以及运行结果展示…

【Redis】基于Redission实现分布式锁(代码实现)

目录 基于Redission实现分布式锁解决商品秒杀超卖的场景&#xff1a; 1.引入依赖&#xff1a; 2.加上redis的配置&#xff1a; 3.添加配置类&#xff1a; 4.编写代码实现&#xff1a; 5.模拟服务器分布式集群的情况&#xff1a; 1.右键点击Copy Configuration 2.点击Modi…

虚拟现实环境下的远程教育和智能评估系统(十)

VR部署测试&#xff0c;采集眼动数据&#xff1b; 经VR内置Camera采集眼睛注视位置后&#xff0c;输出.txt形式的眼动结果&#xff1a; 经处理后&#xff0c;将射线方向和位置投影到视频屏幕二维坐标的位置&#xff1a; 在视频中可视化如下&#xff1a;

Redis的缓存击穿与解决

缓存击穿问题也叫热点Key问题&#xff0c;就是一个被高并发访问并且缓存重建业务较复杂的Key突然失效了&#xff0c;无数的请求访问会在瞬间给数据库带来巨大的冲击。 Redis实战篇 | Kyles Blog (cyborg2077.github.io) 目录 解决方案 互斥锁 实现 逻辑过期 实现 解决方案…

C++ 类与对象的使用要点(超详细解析,小白必看系列)

1.面向过程和面向对象初步认识 C语言是面向过程的语言&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题 例如&#xff1a;洗衣服 C是基于对象的&#xff0c;关注的是对象&#xff0c;将一件事拆分成不同的对象&#xff0c;靠对…

Hadoop3:MapReduce中的Partition原理及自定义Partition

一、默认Partition分区配置 以WC案例来进行验证。 1、设置setNumReduceTasks 修改的代码 这行代码&#xff0c;确定了reduceTask的数量&#xff0c;也确定了分区逻辑 在mapper文件中&#xff0c;打上断点 计算分区的代码 这里会对每一个kv进行计算&#xff0c;然后&#…

STM32F4 STD标准库串口接收中断+空闲中断例程

STM32F4 STD标准库串口接收中断空闲中断例程 &#x1f516;工程基于STM32F446 ✨用惯了STM32CubeMX傻瓜式配置&#xff0c;突然改用标准库写代码&#xff0c;初始化外设内容&#xff0c;总是丢三落四的。 &#x1f4d7;串口初始化配置 void uart_init(uint32_t bound) {//GPIO…

基于51单片机FM数字收音机设计

基于51单片机FM数字收音机 &#xff08;程序&#xff0b;原理图&#xff0b;PCB&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 该系统利主要由STC89C51单片机、液晶显示器、按键、调频收音模块TEA5767、功放LM386组成。 1.收音芯片采用TEA5767模块&…

最新PHP仿猪八戒任务威客网整站源码/在线接任务网站源码

资源介绍 老规矩&#xff0c;截图为亲测&#xff0c;前后台显示正常&#xff0c;细节功能未测&#xff0c;有兴趣的自己下载。 PHP仿猪八戒整站源码下载&#xff0c;phpmysql环境。威客开源建站系统&#xff0c;其主要交易对象是以用户为主的技能、经验、时间和智慧型商品。经…

阿里云域名解析

阿里云域名控制台&#xff1a;https://dc.console.aliyun.com/next/index#/domain-list/all

Mobvista汇量科技解析奥运机会点及营销理念,看广告投放如何抢占先机

四年一度的奥运盛会&#xff0c;作为少有能跨越文化、宗教、种族、行为等各方面差异的体育事件&#xff0c;更能广泛吸引全球观众的目光&#xff0c;成为品牌方和广告主天然的流量磁铁。应用增长平台Mobvista汇量科技为助力各行业开发者、各品牌商家抢占奥运流量&#xff0c;分…

搜维尔科技邀您共赴2024第四届轨道车辆工业设计国际研讨会

会议内容 聚焦“创新、设计、突破”&#xff0c;围绕“面向生命健康、可持续发展的轨道交通系统” 为主题&#xff0c;从数字化、智能化、人性化、绿色发展等方面&#xff0c;探索轨道交通行业的设计新趋势及发展新机遇。 举办时间 2024年7月10日-12日 举办地点 星光岛-青岛融…

串扰(二)

三、感性串扰 首先看下串扰模型及电流方向&#xff1a; 由于电感是阻碍电流变化&#xff0c;受害线的电流方向和攻击线的电流方向相反。同时由于受害线阻抗均匀&#xff0c;故有Vb-Vf&#xff08;感应电流属于电池内部电流&#xff09;。 分析感性串扰大小仍然是按微分的方法…