第一代C语言编译器
在之前的一篇文章我介绍了第一代Unix系统,其实那个系统也已经实现了第一代C语言编译器——legacy-cc。
legacy-cc这个名字是后来人取的,意味已停产的C编译器。实际上,C语言之父丹尼斯里奇真正留下来的是两卷DECTape磁带,分别标有“last1120c”和“prestruct-c”文字。后人把它文本内容导出来整理后,放在了互联网上。
1972年至1973年,Unix操作系统才刚刚诞生,但是已经吸引了大量的用户。在使用过程中,Unix系统很不稳定,但是如果总是用汇编重构会产生大量的工作。因此,作为B语言的继承——C语言,应运而生了。在重构过程中,作为弱类型语言的C语言大放异彩。它连同PAL-IIR汇编和NB语言一起完成了后续几十年的Unix系统开发。
1978年,C语言在操作系统开发领域也取得了巨大成功。同为贝尔实验室的Brian编写了第一套C语言教程《The C Programming Language》,并面向社会推广。这个时期,C语言被称为“K&R C”(或者叫“C78”),其中“K”代表Brian Kernighan,而“R”则代表里奇。这个版本的C开始支持长整型(long)、无符号类型(unsigned)、结构体和联合体的返回类型以及枚举类型(enum)。
1989年,这本书被再版印刷,加入了很多新的C语言语法,并引入了标准库的概念。这个C语言版本变化很大,摈弃了原来的弱类型变量申明,引入了强类型语法。同年,C语言被美国国家标准协会(ANSI)纳入制定标准,所以这个版本的C语言也被称为“ANSI C”(或者叫“C89”)。这个版本的C开始支持函数原型、空指针、国际字符等。
1999年,国际标准化组织再次为C语言制定规范,这个版本的C语言叫“C99”。后续在此基础上制定出了“C11”、“C17”等众多版本。
如今,丹尼斯里奇早已作古,但世界的各个角落还运行着用C语言编写的代码。本篇文章带你穿越时光,重温第一代C语言的辉煌。
一、文件目录
在开头中已经讲到,如今留存下来的C语言编译器只是两卷磁带,其中“last1120c”是里奇将源码从PDP-11计算机迁移出的备份,而“prestruct-c”是结构化改造之前的备份。两者都是独立的C语言编译器的源码,所以我们只研究其中的“last1120c”。
c00.c-c0t.s :这部分文件包含了遍历源码并生成语法树为中间文件的逻辑。
c10.c-c1t.s :这部分文件包含了遍历中间文件并生成机器码的逻辑。所有文件都是文本文件。
其它文件:手动修改语法树让其能够编译成机器码。
打开这些c文件,可以看到编译这份文件的C语言语法和现在有很大区别。有的语法如今已经无法得知具体含义,但是这篇文章还是保留了部分解释。比如,用()代替作为结构体申明语法,用[]代替指针申明等。c文件的存在也说明那个时候C语言也已经完成自举。
而这些s文件则大多数PAL-IIR的汇编语法,只适用于当时迪吉多公司产的PDP系列机器。
二、语法规则
前文已经说过,最早公布的C语言语法规则已经是1978年了。C语言语法规则已经相当成熟,和今天相比已别无二致。然而,我在贝尔实验室的网站上找到了一部分当时没有公开的,写于1974年的C语言语法文档。其署名是丹尼斯里奇。
根据时间,可以推出这个文档记载的C语言语法应该对应了前面的“last1120c”的C语言语法。我整理了一下和今天的C语言规则相比的不同之处——
注释 :
只支持多行注释,并不支持单行注释。
变量名 :
只支持8个字符的变量名,如果是extern修饰的话只支持7个字符。
关键字 :
当时的C语言关键字除了如今常用的几个还包括auto
、register
和entry
。虽然现在也有auto
和register
,但是语义已经发生了很大的变化,以前auto
代表默认存储类型,即除了static
、extern
和register
以外的变量都是auto
存储类型,有时可以省略。register
代表寄存器存储类型,代码中只能有三个该存储类型的变量,且变量类型为int
、char
和指针。entry
只是作为保留没有实现。
初始化 :
变量的申明和初始化必须分开来,即——
int a;
a = 1;