绕不开的 C
我在大学先学习的是 Pascal,然后学习谭浩强的 C,然而听课后练习 Turbo C,却并不太懂,后来通过一本 Pascal 与 C 的比较教程进行的进一步学习,然后购买了原版的 ANSI C,又阅读了 《K&R C 2nd》,至今有些习题依然不太会。话说,任何一个 Real 的程序员都考虑或学习过 C。2023年,我试图在此记录一点关于 C 的事。
C 在哪
C 是从 B 演化诞生的,B 是 BCPL 的简化,BCPL 来自 CPL 提议。
实现 CPL 提议的,最后分为了 Lisp 和 C,两者的差异在于 C 操作数据,Lisp 不区分操作和数据更灵活。毫不夸张说,如果我们拿起放大镜进入任何一台现代计算机,我们都会发现 C 的踪迹。
BCPL 只有一种数据类型——机器字(Word),而为匹配机器的发展,C 在 B 的基础上增加了一些如字符、整数、浮点数等广泛存在于现代编程语言中的数据类型,C 也从 C++ 中轻量反哺了如泛型之类的特性。
在编程语言的班级中,C 虚心学习,足够谨慎,因而在最初的时间就做对了选择,所以在跨越了半个世纪后的今天,依然可以发现它的生命力,并受它的恩惠。
在表达机器的逻辑时,C 是一种比 ASM 语言高级且同样能操作硬件的低层级语言,它的表达与抽象也很完备,也可以编写所有的应用级软件。
而在表达人类的商业逻辑时,C 与自带“电池”(丰富的数据结构)与“充电宝”(垃圾回收机制)的其他高级语言相比,却没有太多优势。
总的来说,它更适合做的是贴近硬件的软件,如操作系统、网络协议。
在我大学的时代,程序员中广泛流传着一句话:所有严肃的程序员都应学习 C,聪明的程序员使用 Delphi (Object Pascal)。
故 C 的位置就是编程的底座。如果用希腊字母 π 的形态来比喻,π 的脚就是 C 和 ASM,它们并为软件世界的地基,深深扎在操作系统中;π 的横线则可以是 C++、Java、Python、PHP、JavaScript、MySQL,Redis 等等其它除 ASM 语言的 any 其他的语言和软件们。
C 从哪里来
我不是个考据派,只是比普通人更着迷一点计算机编程语言的历史,这些计算机科学早期的基本事实不但时代感很强,还晕染着程序员式的幽默。
编程语言的发展的传奇基本也适合这样讲述,这就是说,你可以用比较轻松的来阅读本节,而如何理解 C 的历史也并不影响你在法拉利上写代码。
通用计算机编程语言发端于上世纪 50 年代和 60 年代早期,向 C 之前追溯,会发现 C 源自于 B,幽默的说,B 烤干了 BCPL(Base Combined Programming Langurage)得其精华。
BCPL 源自 CPL,而 CPL 也有剑桥编程语言的意思,它最早叫做 Cambridge Programming Language (a.k.a. Cambridge Plus London),而后改为 Combined Programming Language。
C 用 10 年时间就打好了占领软件世界的基础,在诸多编程语言的竞争中胜出,在后续的 40 多年几乎都是躺赢。
在计算机的世界中,很容易出现赢家通吃的现象,这与计算机的分层抽象特性有关。C 在编程语言世界所处的位置,属于一夫当关千夫莫开的那种。
毫无疑问,最早在特定的硬件上都是机器码和汇编语言,然后人们开始提议实现独立于硬件的通用代数计算机语言。可能是巧合,出现好几个 A 开头的语言,如 ALGOL、Ada,但却没有 A 语言,ALGOL 理论语言阶段的最初命名是 IAL,原是数学大会上为计算机语言脱离特定硬件体系的提议和原则。
计算理论基石科学家 Donald Knuth (a.k.a. 高德纳) 就在 Burroughs 205 上用汇编实现过 ALGOL 58 编译器,ALGOL 58 继而演化为 ALGOL 60,ALGOL 60 又演化为 CPL,然后的事你已经知道了。
通用编程语言从 ALGOL 60 的工作中广泛受益,它几乎标志着计算机科学的诞生,而后才有了今天的软件行业和程序员职业。
时间线是这样的:
1967 年,Matin Richards 在访问 MIT 时对 CPL 做了简化,推出了 BCPL 语言。
1969 年,贝尔实验室的研究员 Kenneth Lane Thompson(后文简称 Ken)设计了 B 语言。
话说那时 1969 年的美国处于一个科技爆棚、社会文化割裂、价值观颠覆、青年人叛逆的时代。在那一年,人类第一次登月,太空探索是很酷的事情,计算机是那个时代很酷的东西,于是 Ken 也做了件很酷的事,它用 B 语言写出一个计算机视频游戏 Space Travel,并灌到了大型机上。但大型机甚至和一件房子那么大,运行很贵,接入很贵,所以 Space Travel 真玩起来也很贵,分时操作系统还没搞成,于是游戏程序还会阻断其它人运行的程序。于是 Ken 想问老板要一台小型机 DEC PDP-10 玩玩,老板说会不会太奢侈了就没同意,Ken 也不死心,就找了台旧的小型机 DEC PDP-7 来重新实现 Space Travel。为让 Space Travel 在旧机器上运行,Ken 花了一个月用汇编为这台机开发了一个操作系统,叫做 Unics,在开发过程中,他也逐步沉淀了一些程序,就包括进一步简化 BCPL 后的 B 语言,这个操作系统就是原始的 UNIX 内核程序、一个编译器、一个命令解析器(Shell)、及一个小的文件系统。
话说 Ken 之所以做这些的原因,就是源自于他去年和同事实现 MULTICS 这个复杂系统积累的经验和郁闷,MULTICS 的目标很宏伟,希望能实现三百人同时接入大型机,同时运行上千个程序。当然,最后这大型项目如大象一般陷入了“焦油坑”,虽然做完了,但称不上成功。而在小型机上实现的 Unics 可谓对完善与复杂的反思,相比体系庞大的硬件和软件,实现更小的程序更不容易出错,而把小的功能专一的程序相互配合起来,则也可以完成宏伟的工作。
1971 年之前,Ken 已经告诉了他的同事 Dennis MacAlistair Ritchie(后文简称 dmr),他做了个游戏很酷,很想继续在新的小型机上玩,dmr 也觉得在新小型机上玩 Space Travel 会很酷,所以加入了 Ken 的操作系统的开发。后来 DEC PDP 10 机器升级到了 PDP 11,原本单一的数据类型计算机字分为了不同规格,有 占 1 字节的字符,占 2 字节的整数,及占 4 字节的浮点数。只有机器字的 B 语言显得不够用了,dmr 觉得 Space Travel 不能在新机器上运行这件事不够酷,于是看了看 BCPL 和 B 语言 的实现,盘算着将 B 适配这台新机器,于是增加了字符、整数、浮点数,改进的 B 命名为 C 语言。
至此,C 语言诞生了。
为了把 C 搞得更酷,Ken 和 dmr 开始用 C 重写 Unics,之后改名为 UNIX。于是,C 和 UNIX 聚齐了,硅基文明大厦的地基打好了。
可能连 C 创始人都不曾预料,C 定位简洁通用,让它在后面的二十年中风靡全世界,另一个成果 UNIX 也形成了 POSIX 标准,之后编程的传奇才得以在 GNU/Linux、BSD/MacOS、WinNT 中继续续写。
更细节的 C 的历史可见于欧洲的大学教授的课堂和贝尔实验室的回忆中,C 之后的版本特性,扩展和库一直在扩充,用 C 开发的软件中,有的随按规范一直在更新,有的用于教学用途,这些创造者们或已经老去或离开了这个世界,但他们传奇的人生背后的思想和作品一起留在了这个世界。
C 会去哪儿
如果你和我一样好奇过“未来学”,你会发现还没有人可以预言未来,但却有人不断预言未来。
适合用 C 实现的东西已用 C 实现。目前 C 已是主流操作系统(Unix/Linux/Windows/macOS)、软件库(lib)、软件包(packages)的基石,从操作系统到编程语言,到数据库和中间件,这也意味着 C 所能构建的一切非常广泛。比如 UNIX、Linux、Git、PHP、Apache、MySQL、SQLite、Lua、Redis、Memcached 等,这些软件从底层到编程语言到中间件,对现代开发都足够自洽,现代应用级软件,都是在 C 开发的软件上开发的。
不适合用 C 实现的未来也不会用 C 实现。从职业的角度上说,至少在 2023 年的当下——C 诞生 50 多年的今天,广泛的等同 C 程序员的职位一般会演化为 C++ 职位,除非业务强依赖于 C,否则不会只需要 C 程序员。这意味着,市场并没有那么多的要求纯 C 语言的程序员职位,也意味着,即使即使你非常熟悉 C,但不熟悉其他语言(即使你能很快学会其他语言),也很可能找不到 C 相关的工作。(也有例外,强如 Linus 的人除外);另一方面,一个 C 专家肯定得是一名 Real 程序员且且英文良好,因为 C 程序员绕不开操作系统和网络的原始文档,虽然部分文档中某些重要的部分被不完整的翻译为了中文,但整体并没有实时更新的中文版本。如果公司需要 C 程序员维护现有程序,中文文档也还够,从 Unix/Linux 入手需看看 POSIX、多线程、Socket、异步编程、并发模型,性能调优,网络协议、网络安全,从 Windows 入手需懂得 Windows API 和 SDK,GUI 编程,Direct X 编程。如果公司需要创立新产品,核心部分很可能没有中文文档。
值得注意的是,我关于职业个人观点也许只适用这几年,未来如果 C 程序员就业市场发生了变化,比如新的技术兴起,IoT、边缘计算、穿戴设备,旧的技术没落都有周期,自也不必惊讶。世界本就是三十年河东、三十年河西。对于 C 来说,未来职位趋势是 L 型减少的,但已经入局的人始终可以稳固掌控计算机系统层级的东西,六十年也并不久,计算机结构依然是冯·诺依曼式的,CPU(Cache) + Memory(RAM+ROM) + Storage + IO Device
,没有变化过。
所以,假设你的工作正在使用 C,这里分两种情况:你从事的领域指定使用 C,比如芯片、实时系统、 C 嵌入式开发、游戏引擎、音频、视频算法等,你可以继续在行业潜心发展;你从事的业务你所在公司用 C 实现的,但行业内还有其他语言实现,这种情况下,不妨未雨绸缪,关注系统编程语言的发展,比如考虑学习 Rust,相比其他语言,它有着结构化的竞争优势还呈快速增长趋势,已被 Linux 和 Windows 和 Web 世界接纳的系统编程语言,比如考虑学习 Go,它的作者之一,也是 C 的作者之一。
使用 C
C 优秀,但 C 也有不少陷阱与缺陷;我在其他语言的编程习惯是,只用最通用和最简单的特性,但在 C 这里会不适用了,C 的特性是为编写操作系统极致控制机器而生,C 总会使用也应使用到。
使用前
如果有本书叫做《C 的食用指南》,那么会分为几块来讲述:首先是准备食材,其次是烹饪调味,最后即可起锅食用。
准备食材。最需要了解的是 C 食谱——语法和库。遵循这些,即可编写 C 程序源代码,代码介质不重要,大脑里、写在纸上、输入在文本编辑器中——重要的是遵循语法并利用库表达程序的思想。
C 语法和库是世界标准,经历了多个版本,从最初的 K&R C、ANSI C,到 C99,C11 等,其主要成分基本不变(计算机体系结构也没有变,只是现代计算机系统相互组合为分布式了),所以一个较懒的做法是只学习一种旧的却广泛使用的标准,就是 C99。目前来看此版本会保持向后兼容,有很多库并不追新就会停留在 C99。
烹饪调味。这需要选一口好锅。即编译、调试、验证,看看程序功能是否满足预期。在进入 C 语法的讲述前,程序员们喜欢挑选一款自己喜欢的文本编辑器,这可不是一件简单的事,传说不通阵营还发生过”圣战“,可见这个过程充满仪式感,就好比哈利波特在奧利凡德商店挑选魔杖一般。如果你对此没把握或不熟悉,可以进入 C 的工具和 C 编译器中先准备准备再继续阅读。有了文本编辑器和编译器,则可以开始编译、调试、验证,一个有追求的厨师,会反复做,反复测试。
起锅食用。重复烹饪调味直到程序达到了预期后,会把得到的菜端上桌子供客人食用。菜就是编译出的最终程序。客人或许不知道你是怎么做出来的,但一定能试出菜好不好吃。
hello world
语言的创造者们,总是以 hello world 程序开始讲述,K&R C 的 hello world 程序如下,就是我们常说的源代码。
#include <stdio.h>
main()
{
printf("hello, world\n");
}
以上文本文件通过构建会生成可执行文件,构建又可分为编译和链接两步,满足操作系统格式的可执行文件则能在操作系统中运行了。
构建(Build) = 源代码(source code)->(编译 Compile)->目标代码(object code)->(链接 Link)->可执行文件
学习使用 C 需要亲自进行构建实践,根据天赋的差异,每个人可能需要 Build 几百次到几万次不等,才会真正学会它。
假设你在 Windows 上使用,我会推荐 EverEdit 文本编辑器,它自带 C 模式中含有 TCC 编译器,是学习 C 编程的简单起点。可以通过 TCC 菜单下的 Run,运行这段代码。TCC Run 背后省略了上述过程,直接运行了生成的可执行文件。
C 语法
系统的讲述 C 语法这件事,已有非常好的出版物和在线参考,我已经将它们放在了附录部分。
C 工具
文本编辑器
文本编辑器,是初学 C 推荐的,IDE 是工作使用到。在不同的操作系统中,有许多花钱的和不花钱的。
一般不花钱的工具都需要花时间,需要自行配置一下或几下才好用,如 Emacs、VIM、NetBeans、Eclipse、VSCode、Code::Blocks、CodeLite 这几个都是跨平台的,现在 VSCode 有一统天下之势。
也有几个平台专属的好用的:Windows 上毫无疑问选 Microsoft 家的 Visual Studio Community Edition,复杂且完善,它由多个产品合并起来,其前身支持 C 的组件叫做 Visual C++ Express;被时代遗忘的源自名门 Borland Software 的被 Embarcadero 收购的 CodeGear 公司的 C++ Builder Community Edition;BloodShed 的 Dev C++ 5 之前的旧版本也不错的,虽有 bug,但在学校学习 C 是够的。Linux 上 KDevelop 和 Geany 也是有不少人用,macOS 上的 XCode 也是可以写 C 的,但更推荐用它写 Swift。
相比免费的,花钱的 IDE 更自有妙处:如宇宙第一IDE——Visual Studio、IDE中的山王工业 JetBrain CLion,文本编辑器顶流 SublimeText,十年磨一剑的国产文本编辑器 EverEdit,一次付费终身可用的业界良心等,都可以方便的编辑 C 代码。如果对文本编辑器感兴趣,可参考笔者文本编辑器系列文章。
C 编译器
C 编译器由于和平台耦合,都难跨平台。各大操作系统公司也都有自己的 C++ 编译器,一般在什么平台就用官方的就好。
- Windows 平台:毫无疑问是 MSVC 最棒,需要利用针对 Intel CPU 优化的也有使用 Intel 公司的。GCC 的实现有 MingW 和 Cygwin,Cygwin 需要一个 dll 中间层,MingW 演化为了 MingW64。
- Linux 平台,一般使用 GNU GCC 家族。
- macOS 平台,以前用 GCC,现在用 Clang + LLVM。
一些著名 C 语言编译器,也有人用,比如大学时代使用的命令行界面的 Borland Turbo C,小而快的 Tiny C Compiler,自带 IDE 用于教学的 lcc、Windows 的 Pelles C、Watcom C/ Open Watcom C,小型机的 IBM C Compiler,如果使用 Qt 的话,自带 QtCreator 也是不错,但更 C++。
C 更多
C 的思想
其实所谓思想,大多是成功后总结出来的,使用了未来函数,参考价值不大。思想是通过细节传播的,C 诞生于 UNIX,可以从学习《UNIX编程环境》开始进入。
C 的书籍
- 《The C Programming Language, Second Edition》, Brian W. Kernighan, Dennis M. Ritchie:你可以一开始就看一遍,能懂多少是多少,接着每隔几年再去读一遍。
- 《C 语言程序设计:现代方法》《C Primer Plus》:这两本可以选择一本读就好了,我个人喜欢前者,但也有很多人喜欢后者,其实只是萝卜青菜的问题,它们各自都非常OK。
- 《C 和指针》《C 陷阱与缺陷》《C 专家编程》:这三本经常被放在一起提,是因为它们还有其他书籍所不含的东西,去到了其它书没有到过的深度来探讨问题。可以在有实际工作经验后逐步阅读。其中,《C 和指针》非常全面,可以作为复习 C 使用;《C 专家编程》讲述了 C 的历史和设计。
- 《编写安全的代码》:这本是微软程序员必读之一
- 《代码阅读》:这本讲解了阅读代码的技术,如果学习开源代码觉得很困难,不妨读完这本再出发。
C 参考
- C 参考手册 - cppreference.com
- C 文档 - 入门、教程、参考。 | Microsoft Learn
- C 标准:ISO/IEC JTC1/SC22/WG14 - C: Approved standards (open-std.org)
- C 历史 (贝尔实验室)
- The C Book - Table of Contents (gbdirect.co.uk)
- Linux C编程一站式学习 (akaedu.github.io):许可证是 GFDL
- C-FAQ:可以用作招聘题库
参考
反馈
如何你觉得本文有用或有趣,或者相反很想吐槽,或者本文有疏漏,都欢迎在下面留言反馈。