编程精粹—— Microsoft 编写优质无错 C 程序秘诀 04:对程序进行逐步跟踪

本文主要是介绍编程精粹—— Microsoft 编写优质无错 C 程序秘诀 04:对程序进行逐步跟踪,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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


不记录,等于没读。本文记录书中第四章内容:对程序进行逐步跟踪。


前两章我们讲到了 断言为子系统设防 这两种自动化检查方法,这两种检查措施都很有价值。我们也说过,不要等待错误发生。要“挨门挨户”的搜查错误:在程序中加上能够积极地寻找这种问题的调试代码。
这里隐含着一个条件,你必须知道哪里可能会发生错误,也就是说:事先,你得知道错误“长什么样”。如果你不知道错误是什么,就抓不到这个错误。比如你只在门窗上安装了报警器,那么当小偷从地下室的开口进入,就不会引起警报。
保证家中物品不被偷走的可靠办法是:在小偷有可能光顾的期间内呆在家里。防止错误进入程序的办法也是这样,在 最有可能出现错误 的时候,密切关注。因此,找到错误的最佳方法是使用调试器逐步执行所有新代码。通过逐步执行每条指令,关注数据流,可以快速发现表达式和算法中的问题。专注于数据而不是指令,可以从另一个角度来审视代码。逐步调试代码虽然花时间,但远没有大多数程序员想象的那么耗时。

那么,什么时候是 最有可能出现错误的时候?
答案是:在编写或修改程序的时候,最可能出现错误,我们要认识到这一点的重要性。编写无错代码的最好办法是在编译时对其进行详尽的测试。

我们在第一章中介绍了 黑箱 测试。测试者给程序提供大量的输入,然后检查输出,如果测试者认为相应的输出没有问题,那么则认为程序没有问题。

黑箱测试的缺陷在于,除了提供输入和检查输出之外,测试者再没有别的方法对代码进行测试。给一些输入,得到了正确的输出,并不能保证代码是正确的。就像你问一些问题,倾听对方回答,并不能确定对方是不是疯子,因为你不知道对方脑袋里在想些什么。

上面的这些限制对程序员来说都不是什么难事。程序员可以在代码中设置断点,一步一步跟踪代码的运行,观察输入变为输出的过程。程序员测试程序最好的办法是对代码进行逐条跟踪,对中间的结果进行认真的查看。

不要等到出了错误再对程序进行逐条地跟踪

  • 对正常处理程序逐条跟踪

  • 对错误处理部分逐条跟踪

  • 对程序中每一条可能的路径逐条跟踪(明显的路径:ifswitch,其它路径:&&||?:

习惯于对代码进行逐条跟踪会产生一个有趣的现象,即他们很快就会学会编写较小的容易测试的函数,因为对大函数进行逐条地跟踪非常痛苦。

对代码进行逐条跟踪的真正作用是:可以使我们观察到数据在函数汇中的流动

如果单步执行仍不能得到执行的细节,那么对于关键部分的代码要进行汇编指令级的逐条跟踪

小结

  • 代码不会自己生出错误,错误是程序员的产物,编写新代码或者修改现有代码都可能产生错误。如果想发现代码中的错误,最好的方法是逐条跟踪代码。
  • 逐条跟踪代码并不会花费太多时间,与返工带来的影响相比,这点时间划算的很。
  • 一定要对每一条代码路径进行逐条的跟踪,尤其是错误处理部分。&&||?: 运算符,它们每个都有两条代码路径需要测试。
  • 当需要汇编级别的逐条跟踪时,不要回避。

题外话

本书第一稿出版于 1993 年,当时软件工程领域还没有 敏捷开发测试驱动开发 等概念(这些概念大范围被认可是在 2000 年左右)。作者写这本书的时候遵循一个理念,那就是只介绍那些并不广为人知,没有被广泛实践,也没有被广泛使用的概念和开发哲学。那个时候作者已经认识到测试的重要性,提倡编写较小的容易测试的函数。在当今 (2024 年) 来看,这是再平常不过的事情,甚至,对于“防止错误进入程序”,我们也有了更号的方法,那就是 测试驱动编程 。我在之前的博文《C 嵌入式系统设计模式 01:软件开发概述》中描述过自己对测试驱动开发的见解,现摘录如下:

开发无缺陷软件的最新技术是一种称为 测试驱动开发 (TDD) 的敏捷实践。

无缺陷的软件 (defect-free software)表示该软件没有任何错误、缺陷或问题,能够按照预期的功能和需求正常运行。

“开发无缺陷的软件?这怎么可能!”换做以前的我,不假思索地就会如此断言。但那时的我,从没有想过,我给出的这个结论,依据是什么。事实上,我没有依据,我连脑子都没动过一下,就草率地下了这样的判断。

而现在,我开始反思这个问题,一方面是因为我的思维方式发生了转变,不再轻易地给出片面的结论;另一方面,是因为我尝试了测试驱动开发。尽管我依然认为开发完全无缺陷的软件是一个难以企及的目标,但测试驱动开发确实让我看到了这一目标的可能性。它让我对我的软件质量越来越有信心。

在尝试测试驱动开发的早期,最难以理解、最难以坚持的可能是被称为 Bob Martin 的测试驱动 3 条原则:

  1. 不允许编写任何产品代码,除非它可以让失败的单元测试通过。
  2. 不允许编写任何足以导致失败的更多的单元测试,编译失败也算失败。
  3. 不允许编写任何足以让单元测试通过的更多的产品代码
  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

首先,你需要先编写单元测试,这就是为什么叫做测试驱动的原因之一。但是根据规则 2,你不能写太多的单元测试,一旦单元测试代码无法编译或者断言失败,就必须编写产品代码。但是根据规则 3,你只能编写恰好使单元测试编译通过或者断言通过的产品代码,而不能再多!

当我年轻时,我曾尝试测试驱动编程,先后尝试过两次,都因为第 2 和第 3 条原则而放弃,我觉得它很愚蠢!如果你没有尝试过测试驱动开发,也许无法体会到我当时的感受。没有关系,我只是想分享一下我曾经的态度和后来的变化。

随着年龄的增长,我的思维方式发生了转变,我收起了排斥和轻视的态度,放下成见,严格遵守测试驱动原则来编写一个程序。然后,我被测试驱动开发迷住了。

我开始认识到,那些我曾经认为愚蠢的规则,实际上并不愚蠢,而是构建稳健软件必不可少的基础,坚持这样的原则,能确保我的代码在全面且细致的测试用例中得到验证,换句话说,能产生彻底测试过的产品代码。

彻底测试,是每一个软件开发者的理想目标,只有达到这一条件,我们才有可能触及编写无缺陷软件的可能。很多时候,我们都在竭尽全力追求彻底测试,但受限于大脑的逻辑和记忆能力,总是会遗漏某些测试路径。然而,遵守测试驱动开发的 3 条原则,可以帮助我们系统化地进行测试,确保每一个细节都得到了验证。只是遵守 3 条原则,就能实现如此的效果,这真是太划算了。


题外话 2

如果你对测试驱动感兴趣,想详细了解,推荐阅读《测试驱动的嵌入式 C 语言开发》。这本书的原作者和译者,给予他们再多的赞美都不为过。






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

这篇关于编程精粹—— Microsoft 编写优质无错 C 程序秘诀 04:对程序进行逐步跟踪的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1072440

相关文章

uniapp接入微信小程序原生代码配置方案(优化版)

uniapp项目需要把微信小程序原生语法的功能代码嵌套过来,无需把原生代码转换为uniapp,可以配置拷贝的方式集成过来 1、拷贝代码包到src目录 2、vue.config.js中配置原生代码包直接拷贝到编译目录中 3、pages.json中配置分包目录,原生入口组件的路径 4、manifest.json中配置分包,使用原生组件 5、需要把原生代码包里的页面修改成组件的方

用Microsoft.Extensions.Hosting 管理WPF项目.

首先引入必要的包: <ItemGroup><PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" /><PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" /><PackageReference Include="Serilog

Java面试八股之怎么通过Java程序判断JVM是32位还是64位

怎么通过Java程序判断JVM是32位还是64位 可以通过Java程序内部检查系统属性来判断当前运行的JVM是32位还是64位。以下是一个简单的方法: public class JvmBitCheck {public static void main(String[] args) {String arch = System.getProperty("os.arch");String dataM

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

16.Spring前世今生与Spring编程思想

1.1.课程目标 1、通过对本章内容的学习,可以掌握Spring的基本架构及各子模块之间的依赖关系。 2、 了解Spring的发展历史,启发思维。 3、 对 Spring形成一个整体的认识,为之后的深入学习做铺垫。 4、 通过对本章内容的学习,可以了解Spring版本升级的规律,从而应用到自己的系统升级版本命名。 5、Spring编程思想总结。 1.2.内容定位 Spring使用经验

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

一道经典Python程序样例带你飞速掌握Python的字典和列表

Python中的列表(list)和字典(dict)是两种常用的数据结构,它们在数据组织和存储方面有很大的不同。 列表(List) 列表是Python中的一种有序集合,可以随时添加和删除其中的元素。列表中的元素可以是任何数据类型,包括数字、字符串、其他列表等。列表使用方括号[]表示,元素之间用逗号,分隔。 定义和使用 # 定义一个列表 fruits = ['apple', 'banana

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述

气象站的种类和应用范围可以根据不同的分类标准进行详细的划分和描述。以下是从不同角度对气象站的种类和应用范围的介绍: 一、气象站的种类 根据用途和安装环境分类: 农业气象站:专为农业生产服务,监测土壤温度、湿度等参数,为农业生产提供科学依据。交通气象站:用于公路、铁路、机场等交通场所的气象监测,提供实时气象数据以支持交通运营和调度。林业气象站:监测林区风速、湿度、温度等气象要素,为林区保护和

企业如何进行员工的网络安全意识培训?

企业网络安全意识培训的重要性         企业网络安全意识培训是提升员工网络安全素质的关键环节。随着网络技术的快速发展,企业面临的网络安全威胁日益增多,员工的网络安全意识和技能水平直接关系到企业的信息安全和业务连续性。因此,企业需要通过系统的网络安全意识培训,提高员工对网络安全的认识和防范能力,从而降低企业在面对潜在安全风险时的损失和影响。 企业网络安全意识培训的方法         企