
前言
C编程语言已经存在很长时间了——它的权威参考资料是其创建者Kernighan和Ritchie写的一本书[1978]。从那时起,C语言就开始被大量应用。用C语言编写的程序和系统无处不在:个人计算机、电话、照相机、机顶盒、冰箱、汽车、大型机、卫星……基本上在任何有可编程接口的现代设备中都能找到。
与C程序和系统的普遍存在相比,人们对C语言的认知和了解要少得多。即便是经验丰富的C程序员,也会对C语言的现代演变表现出一定程度的知识缺乏。一个可能的原因是,C语言被看作一种“容易学习”的语言,它允许缺乏经验的程序员快速地编写或复制代码段,这些代码段至少看起来是在做它应该做的事情。在某种程度上,C语言并没有激发用户学习更高层次知识的积极性。
本书的目的是改变这种普遍的态度,所以它的内容分为4级,以反映对C语言和编程的熟悉程度。这种结构可能与读者的一些习惯相违背,特别是,它将一些困难的主题(如指针)分成不同的层次,以避免过早地向读者提供错误的信息。我们稍后将更详细地解释本书的组织结构。
一般来说,尽管本书会提出许多普遍适用的思想(也适用于其他编程语言如Java、Python、Ruby、C#或C++),但本书主要讨论C语言中特有的或者在用C语言编程时具有特殊价值的概念和实践。
C语言的版本
正如本书的书名所提示的那样,今天的C语言与它的创建者Kernighan和Ritchie最初设计的C语言(通常称为K&R C)不同。特别是,它经历了一个重要的标准化和扩展过程,现在由ISO(国际标准化组织)进行推动。这导致了在1989年、1999年、2011年和2018年一系列C标准的发布,它们通常被称为C89、C99、C11和C17。C标准委员会做了大量工作来保证向后兼容,比如用早期版本(如C89)编写的代码应该使用新版本的编译器编译成语义上等价的可执行文件。不幸的是,这种向后兼容产生了我们不希望看到的副作用,即那些原本可以从新特性中获益的项目没有动力来更新自己的代码库。
在本书中,我们将主要参考JTC1/SC22/WG14[2018]中定义的C17,但是在撰写本书时,一些编译器并没有完全实现这个标准。如果你想编译本书中的示例,至少需要一个可以实现C99大部分功能的编译器。对于将C11添加到C99所要做的修改,使用一个仿真层(比如我的宏包P99)就足够了,该软件包可在http://p99.gforge.inria.fr上找到。
C和C++
编程已经成为一种非常重要的文化和经济活动,C语言仍然是编程界的一个重要元素。与所有人类活动一样,C语言的进步是由许多因素驱动的:企业或个人的利益、政治、美、逻辑、运气、无知、自私、自我(这里加上你的主要动机)。因此,C语言的发展不是也不可能是理想的。它存在缺陷和人为雕琢的成分,只能通过其历史和社会背景来理解。
C语言开发背景的一个重要部分是它的姊妹语言C++的早期出现。一个常见的误解是,C++是通过添加自己的特性而从C演化而来的。尽管这在历史上是正确的(C++是从非常早期的C语言发展而来的),但它们在今天并不是特别相关。事实上,C和C++在30多年前就已经从一个共同的祖先中分离出来,并且从那以后一直在独立地发展。但是这两种语言的演变并不是孤立发生的,多年来,它们一直在交流和采纳彼此的理念。一些新的特性,比如最近添加的原子性和线程,是在C和C++标准委员会的密切协作下设计的。
尽管如此,C和C++仍然有许多不同之处,而且本书中所讲的全部内容都是关于C的,而不是C++。书中所给出的许多代码示例甚至不能用C++编译器编译。因此我们不应该把这两种语言的起源混为一谈。
要点A C和C++是不同的:不要将它们混淆。
注意,当你阅读本书的时候,你会遇到很多如上所示的要点。这些要点总结了特性、规则、建议等。在本书的末尾有一个包含了这些要点的列表,你可以把它作为一个备忘单。
要求
为了能够从本书中获益,你需要满足一些基本要求。如果你对其中任何一个不确定,请先获取或学习它们;否则,你可能会浪费很多时间。
首先,如果不练习,你就无法学习一门编程语言,所以你必须有一个适当的编程环境(通常是在PC或笔记本电脑上),你必须在一定程度上掌握它。这个环境可以是集成的(一个IDE)或者是一组独立的实用程序。平台提供的内容千差万别,因此很难给出具体建议。在类似于UNIX的环境(如Linux和苹果的macOS)中,你可以找到诸如emacs
和vim
之类的编辑器,以及诸如c99
、gcc
和clang
之类的编译器。
你必须能够执行以下操作:
1. 浏览文件系统。计算机上的文件系统通常按层次结构组织在目录中。你必须能够浏览它们来查找和操作文件。
2. 编辑程序文本。这与在字处理环境中编辑字母不同。你的环境、编辑器或它所调用的任何东西都应该对编程语言C有基本的理解能力。你会看到,如果你打开一个C文件(扩展名通常为.C
),它可能会突出显示一些关键字,或者帮助你根据{}
的嵌套来缩进代码。
3. 执行程序。你在这里看到的程序一开始是非常基础的,不会提供任何图形功能。它们需要在命令行中启动。编译器就是这样一个例子。在像UNIX这样的环境中,命令行通常被称为shell,其在控制台或终端上启动。
4. 编译程序文本。有些环境提供用于编译的菜单按钮或键盘快捷键。另一种方法是在终端的命令行中启动编译器。这个编译器必须遵照最新的标准,不要把时间浪费在不适宜的编译器上。
如果你以前从未编写过程序,本书学起来会很难。了解以下内容会有所帮助:Basic、C(历史版本)、C++、Fortran、R、bash、JavaScript、Java、MATLAB、Perl、Python、Scilab等。但是,你可能有一些其他的编程经验,甚至可能没有注意到。许多技术规范实际上是用某种专用的语言编写的,可以作为一种类比,例如,用于Web页面的HTML和用于文档格式化的LaTeX。
你应该知道以下概念,尽管它们在C语言中的确切含义可能与你所学环境中的有所不同:
1. 变量——保存值的命名实体。
2. 条件句——在一个精确的条件下做某事(或不做某事)。
3. 循环——按一定的次数(或者直到满足某个条件为止)重复做某事。
练习和挑战
在本书中,你将看到一些练习,这些练习是为了让你思考所讨论的概念。最好在阅读本书时完成练习。还有一类叫作“挑战”。这些通常要求更高。你需要做一些研究,甚至要了解它们是什么,解决方案不会自己出现:这需要努力。完成挑战要花很多的时间,有时要几个小时甚至几天,这取决于你对工作的满意程度。这些挑战所涉及的主题来自我个人对“有趣问题”的偏好,这些问题来自我个人的经历。如果在学习或工作中有其他问题或涉及相同领域的项目,你应该也可以把它们做得同样好。最重要的是要训练自己,首先从其他地方寻求帮助和想法,然后亲自动手把事情做好。你只有跳进水里才能学会游泳。
本书结构
本书按级别组织,编号从0到3。
第0级“邂逅”总结使用C语言进行编程的基础知识。它的主要作用是提醒你我们所提到的主要概念,并使你熟悉C应用的特殊词汇和观点[1]。最后,即使你在C语言编程方面没有太多的经验,你应该也能够理解简单的C语言程序的结构,并可以开始编写自己的程序。
第1级“相识”详细描述大多数主要概念和特性,如控制结构、数据类型、操作符和函数。它应该能让你更深入地了解运行程序时所发生的事情。这些知识对于算法入门课程和该级别的其他工作来说应该足够了,但值得注意的是指针还没有完全引入。
第2级“相知”深入C语言的核心。它完全解释了指针,帮助你熟悉C语言的内存模型,并使你能够理解C语言的大部分库函数接口。完成这一级别应该使你能够专业地编写C代码。因此,本级别首先对C程序的编写和组织进行了必要的讨论。我个人认为,任何从工程学院毕业、主修计算机科学或C语言编程的人都能达到这个水平。不要满足于比这更低的水平。
第3级“深入”详细介绍特定主题,如性能、可重入性、原子性、线程和泛类型编程。当你在现实世界中遇到这些问题的时候,你可能会发现这里的内容是最好的。作为一个整体,它们对于结束讨论并向你提供C语言方面的全部专业知识是必要的。任何在C语言方面具有多年专业编程经验的人,或者使用C语言作为主要编程语言的软件项目负责人,都应该达到这个水平。
[1]C的一个特殊观点是索引从0开始,而不是像Fortran那样从1开始。