前言
你拿起这本书是为了提升你的编程技能。很好,因为你一定会从这本书提供的实践知识中受益。如果你有丰富的C 语言编程经验,你将学习到良好设计决策的细节,以及它们的优缺点。如果你对C 语言编程还比较陌生,你会找到关于设计决策的指导,并且你会看到这些决策如何一点一点地应用到运行的代码示例中,以构建更大规模的程序。
这本书回答了如何构建C 程序、如何应对错误处理、如何设计灵活接口等问题。
当你对C 语言编程了解得更多时,经常会出现以下问题:
? 我应该返回任何错误信息吗?
? 我应该使用全局变量errno 来做这件事吗?
? 我应该有几个参数很多的函数还是相反?
? 我如何构建一个灵活的接口?
? 我如何构建基本的东西,比如迭代器?
对于面向对象语言,这些问题大多数在《设计模式:可复用面向对象软件的基础》一书中得到了很大程度的解答,该书由Erich Gamma、Richard Helm、Ralph Johnson 和John Vlissides 编著(Prentice Hall,1997)。设计模式为程序员提供了有关对象如何交互以及哪个对象拥有哪种其他对象的最佳实践。
此外,设计模式展示了这些对象如何组合在一起。
然而,对于像C 这样的过程编程语言,大多数这些设计模式无法按照四人组描述的方式实现。C 中没有原生的面向对象机制。虽然可以在C 编程语言中模拟继承或多态,但这可能不是首选,对于那些使用C 编程且不熟悉面向对象语言(如C )以及继承和多态等概念的程序员,可能更愿意坚持他们熟悉的原生C 编程风格。然而,采用原生C 编程风格,并不是所有面向对象设计模式的指导都适用,或者至少对于非面向对象编程语言,无法提供设计模式中所呈现的特定实现思路。
我们所面临的情况是:我们想在C 中进行编程,但不能直接应用设计模式中的大部分知识。本书展示了如何填补这个差距,将实际的设计知识应用于C编程语言中。
我为什么写这本书?
让我来告诉你为什么本书中所汇集的知识对我来说非常重要,以及为什么这样的知识很难找到。
在学校里,我学习了C 编程作为我的第一门编程语言。就像每个新的C 程序员一样,我想知道为什么数组索引从0 开始,我一开始甚至随机尝试如何放置运算符* 和&,来弄懂C 指针里的魔法到底如何起作用的。
在大学里,我学习了C 语法的实际工作原理以及它如何在硬件上转化为位和字节。有了这些知识,我能够编写出表现非常好的小程序。然而,我仍然很难理解为什么较长的代码看起来是这个样子,我肯定永远不会再提出像以下这样的解决方案:
typedef struct INTERNAL_DRIVER_STRUCT* DRIVER_HANDLE;
typedef void (*DriverSend_FP)(char byte);
typedef char (*DriverReceive_FP)();
typedef void (*DriverIOCTL_FP)(int ioctl, void* context);
struct DriverFunctions
{
DriverSend_FP fpSend;
DriverReceive_FP fpReceive;
DriverIOCTL_FP fpIOCTL;
};
DRIVER_HANDLE driverCreate(void* initArg, struct DriverFunctions f);
void driverDestroy(DRIVER_HANDLE h);
void sendByte(DRIVER_HANDLE h, char byte);
char receiveByte(DRIVER_HANDLE h);
void driverIOCTL(DRIVER_HANDLE h, int ioctl, void* context);
这样的代码引会发出许多问题:
? 为什么在结构体中需要函数指针?
? 为什么函数需要那个DRIVER_HANDLE ?
? IOCTL 是什么,为什么我不能使用单独的函数?
? 为什么必须显式的创建和销毁函数?
当我开始编写产品级的应用程序时,这些问题就浮现出来了。我经常遇到一些情况,让我意识到我没有足够的C 编程知识,例如,我无法决定如何实现迭代器,或者决定如何在我的函数中进行错误处理。我意识到,尽管我了解C 语法,但我不知道如何应用它。我试图做一些事情,但只是以笨拙的方式或根本无法实现。我需要的是关于如何使用C 编程语言实现特定任务的最佳实践。例如,我需要知道类似以下的事情:
? 我如何以简单的方式获取和释放资源?
? 在错误处理中使用goto 是个好主意吗?
? 我是应该将接口设计得灵活,还是在需要时直接更改它?
? 我是应该使用assert 语句,还是应该返回错误代码?
? 在C 中如何实现迭代器?
对我来说,非常有趣的一点是,虽然我的有经验的同事们对这些问题有很多不同的答案,但没有人告诉我哪里有了解这些设计决策及其利弊的文档。
所以接下来我转向了互联网,然而我再次感到惊讶:尽管C 编程语言已经存在了几十年,但很难找到这些问题的答案。我发现,虽然有很多关于C 编程语言基础和语法的文献,但很少涉及高级C 编程主题,或者告诉人们如何编写能够应对产品级应用的优美C 代码。
而这正是这本书的用武之地。本书教会你如何提升从编写基本的C 程序,转而到编写考虑错误处理的大规模C 程序,并对未来的需求和设计变化保持灵活的编程技能。本书使用设计模式的概念,逐步为您提供设计决策及其利弊。这些设计模式被应用于运行的代码示例,教会你为什么先前的示例代码会演变成现在的样子。
这些呈现的模式可以应用于任何C 编程领域。由于我来自多线程实时环境的嵌入式编程领域,因此一些模式可能会偏向于该领域。不管怎样,你将会看到这些模式的通用思想可以应用于其他C编程领域,甚至超越了C编程的范围。
模式基础
本书中的设计指导以模式的形式呈现。将知识和最佳实践以模式的形式呈现的想法源自建筑师克里斯托弗·亚历山大的《建筑的永恒之道》(牛津大学出版社,1979)。他使用经过验证的小型解决方案来解决他所在领域的一个巨大问题:如何设计和建造城市。这种应用模式的方法被软件开发领域采纳,其中举办了一些关于模式的会议,如模式语言程序会议(PLoP)等,来用于扩展关于模式的知识体系。特别是由四人组(Gang of Four)《设计模式:可复用面向对象软件的基础》一书(Prentice Hall,1997),对软件开发人员产生了重要影响,使设计模式的概念为软件开发人员广为人知。
但是,什么是模式?市面上有很多定义,如果您对此主题非常感兴趣,那么弗兰克·布施曼(Frank Buschmann)等编写的《面向模式的软件架构:模式和模式语言》(Wiley,2007)一书可以为你提供准确的描述和细节。在本书中,模式为实际问题提供了经过验证的解决方案。本书中呈现的模式具有的结构如表1 所示。
表1:本书中呈现的模式
模式部分 说明
名字 这是模式的名称,应该容易记住。目标是让程序员在日常语言中使用这个名称(就像四人组模式一样,程序员会说:抽象工厂创建对象)。本书中的模式名称以大写字母书写
上下文 上下文部分为模式设置了背景。它告诉你在哪些情况下可以应用这种模式问题 问题部分向您提供了你想要解决的问题的信息。它以粗体字体开始,列出主要的问题陈述,然后详细说明为什么这个问题很难解决(在其他模式格式中,这些详细信息放在一个称为forces的单独部分中)
解决方案 这一部分提供了如何解决问题的指导。它以粗体字体陈述解决方案的主要思想,然后继续详细说明解决方案。它还通过提供代码示例来提供非常具体的指导结果 这一部分列出了应用所描述解决方案的利弊。在应用模式时,你应该始终确认产生的结果是否符合预期
已知用例 已知用例提供了所提出的解决方案在实际应用中是有效的证据。它们还向你展示具体的示例,帮助你理解如何应用该模式
以模式的形式呈现设计指导的一个重要优势是这些模式可以应用在各个场景。如果你面临的是一个巨大的设计问题,将很难找到一个确切的指导文档或解决方案,可以恰好解决这个问题。相反,你可以将巨大且特定的问题视为许多较小和更通用的问题的总和,并通过逐个应用模式来逐步解决这些问题。你只需检查模式所对应的问题描述,选择并应用适合你的问题的模式,并得到一些结果。这些模式应用的结果可能会导致另一个问题,你可以通过接着应用另一个模式来解决。通过这种方式,逐步地设计你的代码,而不是试图在编写第一行代码之前提前产出一个完整的设计。
如何阅读这本书?
你应该对C 语言的基础知识有了一些了解。你应该知道C 语言的语法以及它是如何工作的。例如,这本书不会教你什么是指针或如何使用它。这本书旨在对于一些高级抽象地话题提供提示和指南。
本书中的章节是独立的。你可以按任意顺序阅读它们,并且你可以简单地挑选出你感兴趣的话题。在接下来的一段,你会找到所有模式的概览,从那里你可以跳转到你感兴趣的模式。所以,如果你确切地知道你在寻找什么,直接开始就好了。
如果你不是在寻找某一个特定的模式,而是想要对可能的C 程序的设计选项有一个概览,请通读本书。在本书中,每章都关注一个特定的话题,从如错误处理和内存管理等基本话题开始,然后会转移到更高级和特定的话题,如接口设计或平台独立代码。在每章中,都会介绍与该话题相关的模式,以及逐步展示如何应用这些模式运行代码示例。
本书的部分展示了两个较大的运行示例,在这两个例子里应用了许多的模式。
在这里,你可以学习如何通过应用模式逐步构建一些更加大型的软件。
OReilly 在线学习平台(OReilly Online Learning)
近40 年来,OReilly Media 致力于提供技术和商业培训、知识和卓越见解,来帮助众多公司取得成功。
公司独有的专家和改革创新者网络通过OReilly 书籍、文章以及在线学习平台,分享他们的专业知识和实践经验。OReilly 在线学习平台按照您的需要提供实时培训课程、深入学习渠道、交互式编程环境以及来自OReilly 和其他200 多家出版商的大量书籍与视频资料。更多信息,请访问网站:https://www.oreilly.com/。
联系我们
任何有关本书的意见或疑问,请按照以下地址联系出版社。
美国:
OReilly Media, Inc.
1005 Gravenstein Highway North
Sebastopol, CA 95472
中国:
北京市西城区西直门南大街2 号成铭大厦C 座807 室(100035)
奥莱利技术咨询(北京)有限公司
我们为本书配置了专属网页,在上面列出了勘误表、示例以及其他附加信息。
你可以通过https://oreil.ly/fluent-c 来访问。
如果你对本书有一些评论或技术上的建议,请发送电子邮件到errata@oreilly.com.cn。
要了解OReilly 的图书和培训课程的新闻和信息,请访问我们的网站https://oreilly.com。
我们的LinkedIn:https://linkedin.com/company/oreilly-media。
我们的Twitter:https://twitter.com/oreillymedia。
我们的YouTube:https://www.youtube.com/oreillymedia。
致谢
我想感谢我的妻子Silke,到现在为止她甚至知道什么是模式,我也想感谢我的女儿Ylvi。她们两个都让我的生活更加快乐,确保我不总是坐在电脑前工作,而是享受生活。
没有许多模式爱好者的帮助,这本书不可能面世。我想感谢所有在欧洲程序模式语言会议上的作家工作坊的参与者为我提供关于模式的反馈。特别是,我想感谢以下人员,在会议的所谓的牧羊人过程中为我提供了非常有用的反馈:Jari Rauham?ki、Tobias Rauter、Andrea H?ller、James Coplien、Uwe Zdun、Thomas Raser、Eden Burton、Claudius Link、Valentino Vrani和Sumit Kalra。特别感谢我的工作同事,尤其是Thomas Havlovec,他确保我在模式中的C 编程细节是正确的。Robert Hanmer、Michael Weiss、David Griffiths 和Thomas Krug 花了很多时间审查这本书,并为我提供了如何改进它的额外想法,非常感谢!也感谢OReilly 的整个团队,在使这本书成为现实中给了我很多帮助。特别是,我想感谢我的开发编辑Corbin Collins 和我的产品编辑Jonathon Owen。
本书的内容基于以下在欧洲程序模式语言会议上被接受并在ACM 发表的论文。这些论文可以在http://www.preschern.com 网站上免费访问。
? A Pattern Story About C Programming, EuroPLoP 21: 26th European Conference on Pattern Languages of Programs, July 2015, article no. 53,110, https://dl.acm.org/doi/10.1145/3489449.3489978.
? Patterns for Organizing Files in Modular C Programs, EuroPLoP 20:Proceedings of the European Conference on Pattern Languages of Programs,July 2020, article no. 1, 115, https://dl.acm.org/doi/10.1145/ 3424771.3424772.
? Patterns to Escape the #ifdef Hell, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 2, 112, https://dl.acm.org/doi/10.1145/3361149.3361151.
? Patterns for Returning Error Information in C, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 3, 114, https://dl.acm.org/doi/10.1145/3361149.3361152.
? Patterns for Returning Data from C Functions, EuroPLop 19: Proceedings of the 24th European Conference on Pattern Languages of Programs, July 2019, article no. 37, 113, https://dl.acm.org/doi/10.1145/3361149.3361188.
? C Patterns on Data Lifetime and Ownership, EuroPLop 19: Proc eedings of the 24th European Conference on Pattern Languages of Programs, July 2019,article no. 36, 113, https://dl.acm.org/doi/10.1145/3361149.3361187.
? Patterns for C Iterator Interfaces, EuroPLoP 17: Proceedings of the 22nd European Conference on Pattern Languages of Programs, July 2017, article no. 8, 114, https://dl.acm.org/doi/10.1145/3147704.3147714.
? API Patterns in C, EuroPlop 16: Proceedings of the 21st European Conference on Pattern Languages of Programs, July 2016, article no. 7, 111,https://dl.acm.org/doi/10.1145/3011784.3011791.
? Idioms for Error Handling in C, EuroPLoP 15: Proceedings of the 20th European Conference on Pattern Languages of Programs, July 2015, article no. 53, 110, https://dl.acm.org/doi/10.1145/2855321.2855377.