The New C Standard

周末开始读一本书,《The New C Standard》。尚未出版,但是电子版可以自由下载。

牛人写的计算机方面的书,我想有两个极端。要么像高爷爷的 TAOCP ,厚厚的几大本,还在待续;要么如 K&R 的 TCPL ,薄薄一本,躺在床上轻松读完。

之前,我从没想过,以 C 语言为主题的书,可以砖头样的一大本,1600+ 页。用 80 克纸双面打印,会厚达 9.2 厘米(作者语)。 不过看过副标题,稍微可以理解:An Economic and Cultural Commentary 。感情上升到经济和文化了。敢用这个词的人绝非泛泛之辈。

一不小心读到凌晨五点,居然 Introduction 的一半还没读完 :( ,也就是说,前 100 页都还没正式谈 C 语言呢。

由于是英文版,阅读速度受到了极大的限制。而行文中大量我不认识的单词和长句,迫使我不断的查字典。就这样还能坚持读下来,只能说,这是一本好书,有趣的书。

好吧,简单说,这是一本对 C 的标准及相关层面的一切注条评论的书。

对于每一个点,都在几个方面展开:将其和 C++ 、其它语言对比;描述一般的实现方法;对其做出评论;给出 Coding Guidelines (编码指引)。

下面,我试着将前面介绍部分选译几段,鉴于原文的行文与我来说有许多艰深之处,恐怕很难译的准确,姑且看之吧。


在谈及 C 的新标准时:

C++

许多开发者把 C++ 看作 C 的一个超集,并期待能把 C 代码移到 C++ 里。虽然这本书无意卷入可能是为了让其在 C++ 中更为有效的那些(译注:C 语言标准)重大重新设计的争论中,但本书也尽力驱散 C 是 C++ 的一个子集的神话。对双方而言,它们相互之间平等不分彼此,但是这些章节倾向于关注用 C++ 的翻译器(译注:原文用的 translator 指代把代码翻译成机器可以执行的指令的东西,并不特指编译器或解释器)去翻译 C 代码时需要考虑的问题。

关于 C++ 标准,ISO/IEC 14882:1998(E),对 C99 标准中构造概念能说些什么呢?

  • 措词相同。什么也不用说。
  • 措词相近。在英文语法上有细微的不同,术语上的区别,或是微小的问题。这些通常会被指出来。
  • 措词不同但意思相同。因为词句的排列迥异,很难说它们是一致的。但是含义相同。如果不从 C++ 世界的视野强调的话,很难指出它们从 C 的视野来看有什么不同。
  • 措词不同且含义不同。在这里会引用 C++ 的措词,并对其不同点做一番讨论。
  • 没有和 C99 标准里的句子对应的 C++ 标准里的句子。这经常会出现,因为 C99 标准里构造的概念,有些在 C++ 标准里并不存在,而这一点在之前的句子中已经指出来了。

这里使用一个特别的形式注释源码,即混用了 C /* 风格 */ 和 C++ // 风格 。

C++ 的先驱被称为 C with Classes 。在开发 C++ 的时期,C++ 存在于一个有大量 C 专家和 C 源码的环境下。Stroustrup 的一些引入与 C 不兼容特性的企图遭到了来至 C++ 用户们的抗议。[1310]

C 和 C++ 盘根错节的关系,存在于开发者的心态中,在于厂商只提供单一的翻译器而仅用一个选项来区分语言,也在于构建一个程序时同时使用两种语言翻译生成的模块。这些意味着有必要阐述两者之间任何的差异。

在 1989 年四月的 WG14 会议上,ISO 问了两个问题:(1) C++ 语言是否应该被标准化?(2) WG14 是否是一个应该去做这件事的委员会?关于 (1) 的决定非常接近,一些人争吵说 C++ 还不够成熟到有理由开始标准化,而另一些认为向标准化迈进将使语言稳固下来(对于规范和实现的不断变化会导致用 C++ 做关键任务的应用软件的开发者和头痛)。最终认可的是,应该有一份 C++ 标准,但一致同意不应该由 WG14 来创建这份标准。在 1991 年四月的 WG21 ,ISO C++ 标准委员会成立;两个月之后委员会第一次碰头。

一些 C++ 中附加的背景信息被拿出来。尤其是那些不同的概念,或是术语,用来描述本质上相同的行为。

在一些地方,描述了一些只存在于 C++ 而不见于 C 的概念。这样做的理论基础是基于有些只用 C++ 翻译器的 C 开发者,有可能偶尔用到一些 C++ 里的概念。许多 C++ 翻译器提供了一个 C 兼容模式,这个模式往往提供了更多一点的 C++ 里的概念。这份描述也提供了一些关于,为什么东西到了 C++ 里就变的不一样了的背景。每个人都有一个视点,即使是 C++ 的创造者,Bjarne Stroustrup 。但是最终的说法属于监督语言标准开发的标准主体,SC22 。以下,是最开始的状态。

Resolutions Prepared at the Plenary Meeting of ISO/IEC JTC 1/SC22 Vienna, Austria September 23–29, 1991

Resolution AK Differences between C and C++

Notwithstanding that C and C++ are separate languages, ISO/IEC JTC1/SC22 directs WG21 to document differences in accordance with ISO/IEC TR 10176.

Resolution AL WG14 (C) and WG21 (C++) Coordination

While recognizing the need to preserve the respective and different goals of C and C++, ISO/IEC JTC1/SC22 directs WG14 and WG21 to ensure, in current and future development of their respective languages, that differences between C and C++ are kept to the minimum. The word "differences" is taken to refer to strictly conforming programs of C which either are invalid programs in C++ or have different semantics in C++.

这个状态在第一份 C++ 标准完成后被更新,但是对于 C 标准的修订来说,很难产生重要的影响了。

Resolutions Prepared at the Eleventh Plenary Meeting of ISO/IEC JTC 1/SC22 Snekkersten, Denmark August 24–27, 1998

Resolution 98-6: Relationship Between the Work of WG21 and that of WG14

Recognizing that the user communities of the C and C++ languages are becoming increasingly divergent, ISO/IEC JTC 1/SC22 authorizes WG21 to carry out future revisions of ISO/IEC 14882:1998 (Programming Language C++) without necessarily adopting new C language features contained in the current revision to ISO/IEC 9899:1990 (Programming Language C) or any future revisions thereof.

ISO/IEC JTC 1/SC22 encourages WG14 and WG21 to continue their close cooperation in the future.


其它语言

为什么这本书要讨论其它语言?因为开发者不喜欢把他的整个工作生命耗在一门语言上(或许有些 Cobol 和 Fortran 程序员将这样做)。

C 不是这个世界上唯一的编程语言(虽然有些开发者曾经把它当成唯一语言)。其它语言的特性能够使得开发者对于 C 的精神(设计,滋味,世界观)的理解更加尖锐。一些 C 的概念可能从不同的方式被替换,其它的方式之间也互相关连。

C 中存在的函数性会影响到对算法编码的方式(不要忘记个人差异[1117, 1118] )。因为 C 里怎样去做这件事决定了源码中的段落只能按那种方式写出来;但它们在其它语言中,可以以不同方式来写,并且有不同的运行时的特性 [1119] 。对于开发者来说,赏识那些他们写出来的代码中由 C 语言的特性带来的结果,很难做到。这就像让鱼试图理解在水里和在陆地上的区别一样。

有些概念通行于所有的编程语言,而另一些仅仅存在于 C (通常也会存在于 C++)。有些概念对于特定类型的语言很普通—— 算法、功能、命令式、形式化的,等等。用 C 去做一件事情的方式通常不是达到目的或是得到算法效果的唯一路径。有时 C 有其特有的方式。有时 C 类似于别的语言的做法。有时有些语言以与 C 迥然不同的方式去做事,无论是实现同一个想法,还是用于另一个不同的世界观下。

这里无意声称 C 或其它语言更好或更差,因为它们有自己独特的设计哲学,或是包含了一个特定的概念。这个分类不会刻意的去考察其它语言做了些什么,也不会试图讨论除了这个东西很像或不像 C 之外的关于其它语言的东西。只是从 C 的视点去看其它语言而已。

当开发者从 C 转移到其它语言时,在一年左右的时间里(或者更长时间,这取决于花在新语言上的具体时间),倾向于以 C-like 的风格去使用新语言(这非常像人们在学习英语时,一开始倾向于使用他们母语中的语法和句法;可以说的很流利,听起来也没有障碍)。

笔者针对许多 C 开发者的体会是,他们倾向于认为只有 C 才是值得了解其细节的语言。这个章节不想改变这种看法,也没有涉及这个方面。了解一些其它语言怎么运作的知识没有什么坏处。

有一些经过时间考验的语言,比如说 Cobol 和 Fortran 。虽然 Pascal 和 Ada 可能在怎样编写可维护的、健壮的代码方面有很强的影响力,不过它们只存在于相对较短的时代,出现并消逝。在写作这部作品的时间,有六个 Ada 95 的实现。1995 年的一份语言使用纵览[590] 发生在 DoD 武器系统中有 49.5 M 行 Ada 83 代码( C89 有 32.5 M 行,其它语言是 66.18 M 行)。由于对 Pascal 标准缺乏兴趣,导致人们去问是否应该来提取一份公认的标准(ISO 规则需要一份标准每五年被评审一次)。Java 语言正在侵入嵌入式系统市场(它成为互联网的通用语的期望看起来没有发生)。它很新潮,一直保持在公众的眼球内。Lisp 在它被创造出来的 40 年里,一直都拥有专注的用户。有一篇论文对它的赞誉超过了 C 。[411]

这本书提及的语言的参考资料有:Ada,[645] Algol 68,[1407] APL,[654] BCPL,[1162] CHILL,[656] Cobol,[634] Fortran,[640] Lisp[650] (Scheme[719]), Modula-2,[646] Pascal,[639] Perl,[1438] PL/1,[633] Snobol 4,[521] and SQL.[641]

对于一些实现和 C 完全不同的语言包括:APL,[178] 函数式语言,[1082] 以及 ML.[50]


在论及文化时,书中这样写道:

每一种语言都有一个与之相关联的文化。文化导致思考特定事情,做特定事情时,都有一种特定的方式[1015]。这些最初的选择怎样发生、为何发生,引出了一些有趣的历史背景,而这些在这本书的其它章节讨论。不过这些内容与编码指引的章节无关。

文化这个词用在 C 开发者的一些普通惯例上,或许过于华丽。对于开发者们,即使不给出一些形而上学的大概念,就已经够偏狭和过于自信的了。使用 "惯例" 这个名词既表达出其功能性,又能减少它们被夸大的可能。

"惯例" 可以被想象成一组事情怎样(用 C)去完成的设想及期望的集合。"C 风格" 这个名词有时用来形容这些设想及期望。然而,这个词对于不同的开发者、不同的环境,有许多不同的含义,非常容易导致误解与争论。因此在这本书里极力远离关于风格的概念。

在很多方面,惯例是一个模因机器[125]。(译注:meme machine ,这个词来至于《自私的基因》。meme 指代人类文化进化的基因,一种模仿单位。)开发者阅读已经存在的代码,学习里面包含的思想,潜意识的使用这些思想编写新的代码。许多特定的编写代码的方式不需要对包含它们的程序有用处。他们仅仅需要表现得对编写这些代码的开发者有用就行,或是适合开发者最喜爱的做事情的方式。有时候,开发者不去彻底的分析要写些什么,他们只是跟从他人。软件开发有它自己的时尚和潮流,这一点和其它任何由信息驱动的活动一样[124]。

在观察编码指引中的惯例的效果之前,我们应当先问一下是些什么东西构成了惯例。至于书中提到的指引中给出的推荐,构成惯例的东西被记录在惯用法的章节。开发者不喜欢用科学的方法来处理这个问题。他们工作在一个或多个应用领域,接触各式各样源代码,和其他各种各样的开发者讨论 C 。虽然一些公司可以选择调节他们的指引到适用于指定应用领域与工作环境下的实际情况,但这本书中的指引建议还是试图适用于一般的应用领域。

惯例并不会全部记录下来,有时,开发者甚至不能表述出它们是什么。有经验的开发者有时用 C 方式的表达式去做事情,或者只是我自己感觉是这样。一旦问起这些表达式的含义的时候,他们不能给出一个确切的答案。这种人类行为(知道一些事情但不能表述出它是什么)在实验室里经常出现。

……


在谈及编码指引( coding guidelines )时,关于效率问题,书中这样写:

经验显示,许多开发者相信代码的效率是代码质量的一个重要指标。这个信念不仅仅存在于 C 的文化中,且有着悠久的历史[772]。效率在一些应用领域保留着一些争议,这些编码指引通常以效率为理由来解释一些不良习惯,而这些不良习惯(从操作结果的视野来看)是需要慎重考虑的。

经验显示,一些开发者把源代码视觉上的致密性和翻译后程序运行时的性能同等看待。虽然有一些语言存在这样的相关性(例如,一些 Basic 的实现:几乎是在早期所有玩具电脑上的基于解释运行的 Basic ,都对源代码做实时的翻译),但这并不存在于 C 中。这是一个应该在开发者的教育期被覆盖的译题。(译注:这段让我想起了儿时的 Basic 时代,整天想着把 Basic 程序写的更精巧,以便让它跑的更快)

经验还显示开发者在做一个选择时,很大因素取决于他估计需要做多大的键盘输入量。减少键盘输入的行为包括选择更简短的标识符名字,使用剪切粘贴来复制代码段,使用键盘快捷键,以及创建一些编辑器宏(有时需要的比他们节省下来的更多的时间)。


在写到开发人员对翻译器的期望时,对 C 语言有一段精辟的论述:

  1. C 拥有在效率方面的名声。它能写出程序尽可能是使用处理器的资源。写出这样的代码取决于对处理器以及翻译器如何把 C 语言中的概念映射为机器码这两方面的知识的了解。没有几个开发人员对这些课题有足够的了解以便能始终写出非常有效率的程序。笔者有时预测用自己写的编译器生成的代码都有困难。笔者发现一个普适法则:如果有任何开发人员声称在语句级上有一种使用方法效率最高,都可以很安全的说他错了。语句级的性能开销不需要去争论,交给翻译器去操心好了。

  2. C 拥有在致密性上的名声。每条 C 语句对应的机器码指令的体积比相对于别的语言来说是一个很小的数字。可以说,C 是一种所见即所得的语言,从 C 语言映射到机器码简洁、显而易见。(暂时把优化器在后面做的工作先放在一边)。这个期望曾经被 WG14 的一些成员用作反对等于操作符用于结构类型的论据;那样的话,一条操作就将潜在的生成大量的代码,用于逐条比较每个结构成员。引入 inline 函数声明从一些角度破坏了这一点(这取决于 inline 是被想象成类函数宏的替代品,还是在当前嵌入一个不能被实现成宏的函数)。

  3. C 有着是一种表里如一的语言的名声。开发者通常能预测到他们所写代码的行为。没有什么阴暗角落藏着一些偶然一遇的用法会导致一些语言概念以意想不到的方式运作。虽然 C 委员会不能绝对保证永远不会出现另人惊奇的行为,但他们一直致力于最小意外原则。


翻译这个东西真累,翻的不好,可能还有错误的理解。不过我尽力不在里面夹带私货了 :D 有兴趣的同学去读原版吧。