计算机科学入门
之前我们把重点放在硬件,也就是组成计算机的物理组件,比如电路、寄存器、RAM、ALU、CPU。
但在硬件层面编程非常麻烦,所以程序员想要一种更通用的方法编程,也就是软件。
机器码
之前说过,前 4 位是操作码,简称 OPCODE ,对于我们假设的 CPU ,0010 代表 LOAD_A 指令——把值从内存复制到寄存器 A。
可以想成是英语和摩尔斯码的区别,hello
和 .... . .-.. .-.. ---
是一个意思,只是编码方式不同。
英语和摩尔斯码的复杂度也不同,英文有 26 个字母以及各种发音,摩尔斯码只有 点
"和 线
。
但它们可以传达相同的信息,计算机语言也类似,计算机能处理二进制,二进制是处理器的 母语 ,事实上,它们只能 理解二进制,这叫 机器语言
(machine language)或 机器码
(machine code)。
伪代码
在计算机早期阶段,必须用机器码写程序,具体来讲,会先在纸上用英语写一个 高层次版 ,也叫做 伪代码 ,在这个阶段不需要写真正的程序,而是用中文、英语等语言描述程序的 运行步骤 ,就好像把大象塞进冰箱里。
- 把冰箱门打开
- 把大象放进冰箱里
- 把冰箱门关上
在纸上写好后,用 操作码表 把伪代码转成二进制机器码,翻译完成后,程序可以放入计算机并运行。
助记符
你可能猜到了,很快人们就厌烦了,所以在 1940~1950 年代,程序员开发出一种新语言, 更可读 更高层次 。
每个操作码分配一个简单名字,叫 助记符 (mnemonics), 助记符 后面紧跟数据,形成完整指令,与其用 1 和 0 写代码,程序员可以写 LOAD_A 14
。
汇编器
当然,CPU 不知道 LOAD_A 14
是什么,它不能理解文字,只能理解二进制,所以程序员想了一个技巧,写二进制程序来帮忙。它可以读懂文字指令,自动转成二进制指令,这种程序叫 汇编器 (Assembler)。
汇编器读取用 汇编语言 写的程序,然后转成 机器码 ,LOAD_A 14
是一个汇编指令的例子。
随着时间推移,汇编器有越来越多功能,让编程更容易,其中一个功能是 自动分析 JUMP 地址,原来的程序,比如 JUMP 2 是跳转到内存的 3 号位置,问题是,如果在程序开头多加一些代码,比如在 3 号位置新增一条 LOAD_A 指令,那我希望跳转到的 3 号位置现在就变成 4 号位了,如果这样的操作变多那更新程序会很痛苦!
所以汇编器不用固定跳转地址,而是让你插入可跳转的标签,当程序被传入汇编器,汇编器会自己搞定跳转地址,程序员可以专心编程,不用管底层细节,隐藏不必要细节来做更复杂的工作。
然而,即使汇编器有这些厉害功能,比如自动跳转,汇编只是修饰了一下机器码,一般来说,一条汇编指令对应一条机器指令,所以汇编码和底层硬件的连接很紧密。
汇编器仍然强迫程序员思考:用什么寄存器和内存地址,如果你突然要一个额外的数,可能要改很多代码。
A-0
葛丽丝·霍普 博士也遇到了这个问题,作为美国海军军官,她是 哈佛 1 号 计算机的首批程序员之一,这台巨大机电野兽在 1944 年战时建造完成,帮助盟军作战,程序写在 打孔纸带 上,放进计算机执行。
如果程序里有漏洞,真的就直接用胶带来补"漏洞"。
Mark 1 的指令集非常原始,甚至没有 JUMP 指令,如果代码要跑不止一次,得把带子的 两端 连起来做成循环。
战后,霍普继续在计算机前沿工作,为了释放电脑的潜力,她设计了一个高级编程语言,叫 算术语言版本 0
(Arithmetic Language Version 0),简称 A-0
。
汇编与机器指令是 一 一 对应的,但一行高级编程语言可能会转成几十条二进制指令,为了做到这种复杂转换 Hopper 在 1952 年创造了第一个编译器,编译器专门把 高级语言 转成 低级语言 ,比如汇编或机器码(CPU 可以直接执行机器码)。
我有能用的编译器,但没人愿意用,他们告诉我计算机只能做算术,不能运行程序。——葛丽丝·霍普
如今有上百种语言!可惜的是,没有任何 A-0 的代码遗留下来。
变量
我们用 Python 举例,假设我们想相加两个数字,保存结果。
记住,如果用汇编代码,我们得从内存取值,和寄存器打交道,以及其他底层细节。
但同样的程序可以用 Python 这样写:
a = 1
b = 2
c = a + b
a = 1
b = 2
c = a + b
不用管寄存器或内存位置,编译器会搞定这些细节,不用管底层细节。
程序员只需要创建代表内存地址的抽象,叫 变量
。
这里取名 A 和 B,实际编程时你可以随便取名,然后相加两个数,把结果存在变量 C,底层操作时,编译器可能把变量 A 存在寄存器 A,但我不需要知道这些!
这是个重要历史里程碑,但 A-0 和之后的版本没有广泛使用。
FORTRAN
FORTRAN,名字来自 公式翻译
(Formula Translation),这门语言数年后由 IBM 在 1957 年发布,主宰了早期计算机编程,目前仍然有部分专业需要学习这个编程语言。
平均来说,FORTRAN 写的程序,比等同的手写汇编代码短 20 倍。
然后 FORTRAN 编译器会把代码转成机器码,人们怀疑性能是否比得上手写代码(一般来说,直接写汇编语言虽然慢但是性能最好),但因为能让程序员写程序更快,所以成了一个更经济的选择,运行速度慢一点点,编程速度大大加快。
COBOL
因此最初 FORTRAN 代码只能跑在 IBM 计算机上,1950 年代大多数编程语言和编译器,只能运行在一种计算机上,如果升级电脑,可能要重写所有代码!
因此工业界,学术界,政府的计算机专家在 1959 年组建了一个联盟——数据系统语言委员会(the Committee on Data Systems Languages),Grace Hopper 担任顾问。
开发一种通用编程语言,可以在不同机器上通用,最后诞生了 普通面向商业语言
(Common Business-Oriented Language),简称 * COBOL* 。
为了兼容不同底层硬件,每个计算架构需要一个 COBOL 编译器,最重要的是,这些编译器都可以接收相同 COBOL 代码,不管是什么电脑,这叫 一次编写,到处运行 ( write once, run anywhere)。
COBOL语法接近于英语书面语,设计理念就是让更多人理解与学习。普通人是看懂了,可程序员却看愣了,COBOL语言过于繁琐、冗长,写代码就像写小说一样,让人难受,加之COBOL不能进行科学计算,只能简单运算。COBOL语言虽然很难出bug,格式却过于死板,远没有C语言随心所欲,随着新型语言出现,COBOL语言使用者快速流失,逐渐消亡。
其他编程语言
如今大多数编程语言都是这样,不必接触 CPU 特有的汇编码和机器码,减小了使用门槛,在高级编程语言出现之前,编程只是计算机专家和爱好者才会做的事,但现在,科学家,工程师,医生,经济学家,教师等等,都可以把计算机用于工作。
感谢这些语言,计算机科学从深奥学科变成了 大众化工具 。
同时,编程的抽象也让计算机专家,制作更复杂的程序。
当然,计算机的历史没有在 1959 年结束,编程语言设计的黄金时代才刚刚开始,和硬件一起飞速发展。
在 1960 年代,有 ALGOL, LISP 和 BASIC 等语言。
70年代有:Pascal,C 和 Smalltalk。
80年代有:C++,Objective-C 和 Perl。
90年代有:Python,Ruby 和 Java。
现在Swift, C#, Go 在崛起。
这些编程语言名字只是冰山一角,新的编程语言在不断诞生,新语言想用更聪明的抽象,让某些方面更容易或更强大,或利用新技术和新平台带来的优势,让更多人能快速做出美妙的事情。