在计算机发展的早期,硬件很昂贵,而程序员的成本很低。事实上,程序员的成本低到他们甚至不被称为“程序员”,通常是数学家或电气工程师。早期的计算机用于快速解决复杂的数学问题,因此数学家自然成为“编程”工作的合适人选。
什么是程序?
首先,介绍一些背景知识。计算机本身什么也做不了,因此需要程序来驱动它们的行为。程序可以被认为是非常详细的配方,它接受输入并产生输出。配方中的步骤由对数据进行操作的指令组成。虽然这听起来很复杂,但您可能知道这个语句是如何工作的
1 + 2 = 3
加号是“指令”,而数字 1 和 2 是数据。从数学上讲,等号表示方程的两边“相等”,但是大多数计算机语言都使用某种形式的等号来表示“赋值”。如果计算机正在执行该语句,它会将加法的结果(“3”)存储在内存中的某个位置。
计算机知道如何用数字进行数学运算,以及在机器的内存层次结构中移动数据。关于内存我不会说太多,除了它通常有两种不同的类型:快速/小 和 慢速/大。CPU 寄存器非常快、非常小,并且充当暂存区。主存储器通常非常大,并且不如寄存器存储器快。CPU 在程序执行时,将它们正在处理的数据从主存储器移动到寄存器,然后再移回来。
汇编器
计算机非常昂贵,而人工成本很低。程序员花费无数的时间将手写的数学公式转换为计算机可以执行的计算机指令。最早的计算机具有糟糕的用户界面,有些仅由前面板上的拨动开关组成。开关代表内存中单个“字”中的 1 和 0。程序员将配置一个字,指示存储位置,然后将该字提交到内存。这既耗时又容易出错。

程序员 Betty Jean Jennings(左)和 Fran Bilas(右)在 摩尔电气工程学院操作 ENIAC 的主控制面板。(美国陆军照片,来自 ARL 技术图书馆的档案)
最终,一位 电气工程师 认为他的时间并不廉价,因此编写了一个程序,该程序以人们可以阅读的“配方”形式输入,并输出计算机可读的版本。这是第一个“汇编器”,它引起了很大的争议。拥有昂贵机器的人不希望在人们已经做的事情上“浪费”计算时间;尽管速度很慢并且容易出错。随着时间的推移,人们开始欣赏汇编器的速度和准确性,而不是手工汇编的程序,并且计算机完成的“实际工作”量有所增加。
虽然汇编程序比将位模式拨动到机器的前面板有了很大的进步,但它们仍然非常专业。上面的加法示例可能看起来像这样
01 MOV R0, 1
02 MOV R1, 2
03 ADD R0, R1, R2
04 MOV 64, R0
05 STO R2, R0
每一行都是一条计算机指令,以指令的简写名称开头,后跟指令处理的数据。这个小程序将首先将值 1 “移动”到名为 R0 的寄存器中,然后将 2 移动到寄存器 R1 中。第 03 行将寄存器 R0 和 R1 的内容相加,并将结果值存储到寄存器 R2 中。最后,第 04 和 05 行标识结果应存储在主存储器中的位置(地址 64)。管理数据在内存中的存储位置是编写计算机程序中最耗时且容易出错的部分之一。
编译器
汇编比手动编写计算机指令要好得多;然而,早期的程序员渴望像习惯于编写数学公式那样编写程序。这推动了高级编译语言的开发,其中一些是历史脚注,而另一些至今仍在使用。ALGO 就是这样一个脚注,而诸如 Fortran 和 C 这样的语言仍在用于解决实际问题。

编程语言的家谱,Algol 和 Fortran 家族
这些“高级”语言的引入使程序员可以用更简单的术语编写他们的程序。在 C 语言中,我们的加法汇编程序将写成
int x;
x = 1 + 2;
第一个语句描述了程序将使用的一块内存。在这种情况下,内存应该是整数的大小,其名称是 x。第二个语句是加法,尽管是“向后”编写的。C 程序员会将其理解为“X 被赋值为一加二的结果”。请注意,程序员不需要说将 x 放在内存中的哪个位置,因为编译器会处理它。
一种名为“编译器”的新型程序会将用高级语言编写的程序转换为汇编语言版本,然后通过汇编器运行它以生成程序的机器可读版本。这种程序的组合通常称为“工具链”,因为一个程序的输出直接发送到另一个程序的输入。
编译语言相对于汇编语言程序的最大优势在于从一个计算机型号或品牌移植到另一个型号或品牌。在计算机发展的早期,来自 IBM、Digital Equipment Corporation、Texas Instruments、UNIVAC、Hewlett Packard 等公司的不同类型的计算硬件大量涌现。除了需要插入电源外,这些计算机几乎没有共同之处。内存和 CPU 架构差异很大,将程序从一台计算机翻译到另一台计算机通常需要数人年。
使用高级语言,编译器工具链只需要移植到新平台。一旦编译器可用,高级语言程序就可以重新编译以用于新计算机,而无需进行任何修改。高级语言的编译是真正具有革命性的。

1983 年发布的 IBM PC XT
程序员的生活变得非常美好。使用高级语言表达他们想要解决的问题变得容易得多。由于半导体技术的进步和集成芯片的发明,计算机硬件的成本急剧下降。计算机变得更快、更强大,而且也便宜得多。在某个时候,可能是在 80 年代后期,出现了一种反转,程序员变得比他们使用的硬件更昂贵。
解释器
随着时间的推移,一种新的编程模型兴起,在这种模型中,一种称为“解释器”的特殊程序会读取一个程序,并将其转换为要立即执行的计算机指令。解释器将程序作为输入,并将其解释为中间形式,很像编译器。与编译器不同,解释器然后执行程序的中间形式。这发生在每次运行解释程序时,而编译程序仅编译一次,计算机“按书面”执行机器指令。
顺便说一句,当人们说“解释程序很慢”时,这是感知到的性能不足的主要来源。现代计算机的功能非常强大,以至于大多数人无法区分编译程序和解释程序。
解释程序,有时称为“脚本”,甚至更容易移植到不同的硬件平台。因为脚本不包含任何特定于机器的指令,所以单个版本的程序可以在许多不同的计算机上运行而无需更改。当然,问题是必须将解释器移植到新机器才能实现这一点。
一个非常流行的解释语言的例子是 perl。我们的加法问题的完整 perl 表达式将是
$x = 1 + 2
虽然它看起来和 C 版本非常相似,但它缺少变量初始化语句。还有其他差异(超出了本文的范围),但你可以看到我们可以编写一个计算机程序,它非常接近数学家用笔和纸手写的方式。
虚拟机
编程模型中的最新热潮是虚拟机,通常缩写为 VM。虚拟机有两种类型:系统虚拟机和进程虚拟机。两种类型的 VM 都提供了与“真实”计算硬件的抽象级别,尽管它们的范围不同。系统虚拟机是提供物理硬件替代品的软件,而进程虚拟机旨在以系统独立的方式执行程序。因此,在这种情况下,进程虚拟机(以下简称虚拟机)在范围上与解释器类似,因为程序首先被编译为中间形式,然后虚拟机执行它。
解释器和虚拟机之间的主要区别在于,虚拟机实现了一个理想化的 CPU,并通过其虚拟指令集访问。这种抽象使得编写前端语言工具成为可能,这些工具编译用不同语言编写的程序并以虚拟机为目标。可能最流行和最广为人知的虚拟机是 Java 虚拟机 (JVM)。JVM 最初仅用于 1990 年代的 Java 编程语言,但现在它托管着 许多流行的计算机语言:Scala、Jython、JRuby、Clojure 和 Kotlin,仅举几例。还有其他可能不为人知的例子。我最近才了解到我最喜欢的语言 Python 不是一种解释语言,而是一种 托管在虚拟机上的语言!
虚拟机继续了历史趋势,即减少了程序员表达其领域特定需求的语言问题所需的平台特定知识量。
总结
我希望你喜欢这篇关于软件中一些不太可见的部分的入门读物。你希望我接下来深入研究其他主题吗?请在评论中告诉我。
这篇文章最初发表于 PyBites,经授权转载。
4 条评论