跳过正文

计算机是一个会读会写的印度女工

·118 字·1 分钟
Banson
作者
Banson

相信所有初学者的计算机启蒙都是从类似这样一段 C语言程序开始的:

#include <stdio.h>
int main() {
  int a, b;
  scanf("%d %d", &a, &b);
  int c = a + b;
  printf("%d\n", c);
  return 0;
}

当你在 Dev-cpp 中点击“运行”,屏幕弹出一个黑乎乎的窗口,你的大脑大概是懵的:这就完啦?

还没完。至少向下,我们需要回答“这些文字内容是如何被计算机执行的”,向上我们还会好奇“怎么样才能写出来不那么黑乎乎的程序”;然而此刻,让我们先大概看看第一个问题吧。

图灵机是计算机最简洁、最本质的抽象。贴一下 Wikipedia 对它的描述:

该机器由以下几个部分组成:

  • 一条无限长的纸带TAPE。纸带被划分为一个接一个的小格子,每个格子上包含一个来自有限字母表的符号,字母表中有一个特殊的符号表示空白。纸带上的格子从左到右依次被编号为0, 1, 2, …,纸带的右端可以无限伸展。

  • 一个读写头HEAD。它可以在纸带上左右移动,能读出当前所指的格子上的符号,并能改变它。

  • 一套控制规则数量有限的TABLE(A finite table of instructions)。它根据当前机器所处的状态以及当前读写头所指的格子上的符号来确定读写头下一步的动作,并改变状态寄存器的值,令机器进入一个新的状态。

按照以下顺序告知图灵机命令:

  1. 写入(替换)或擦除当前符号;
  2. 移动 HEAD, ‘L’向左, ‘R’向右或者’N’不移动;
  3. 保持当前状态或者转到另一状态。
  • 一个状态寄存器。它用来保存图灵机当前所处的状态。图灵机的所有可能状态的数目是有限的,并且有一个特殊的状态,称为停机状态。

这样说还是太抽象了。其实(冯诺依曼结构的)计算机就是一个会读会写的工人。

  • 他的面前是一个 一格一格的、每个格子都有编号的、无限长的 纸带;
  • 格子里可能写的是指令
    • 算术指令:“把心里的两个数加起来记住”
    • 读内存指令:“从编号为n的格子里读数并记住”
    • 写内存指令:“把心里的某个数写到编号为n的格子里”
    • 分支指令:“如果心里的某个数大于0, 就把头移到编号为n的格子,从那里继续读指令”
    • 停机指令
  • 格子里也可能只是写了一个简单的数;
  • 工人视力不好,只能看到眼前的这一个格子。

给专业的同学:想象一个单周期CPU/五段流水CPU, 所谓“工人的头”就是Program Counter

这个工人每次都这样工作:

  • 读取看到的指令,按照它说的做
  • 如果是分支指令,就按它的指示把自己的头移到对应位置;如果不是,则把自己的头移到下一个格子上。

对于开头提到的程序,工人大概是这样工作的:

  • 在0号格子读取了指令 “从1000号格子读取一个数,记作a”;把头移到1号格子。
  • 在1号格子读取了指令 “从1001号格子读取一个数,记作b”;把头移到2号格子。
  • 在2号格子读取了指令 “把心里的a和b相加,记作c”;把头移到3号格子。
  • 在3号格子读取了指令 “把心里的c写到2000号格子里”;把头移到4号格子。
  • 在4号格子读到了停机指令。

你可能认为lz太啰嗦了,把1234这些编号写了好多也不嫌烦。这是因为分支结构还没有出现。考虑一段求 $\Sigma_{i=1}^{10} i$ 的程序,它是这样工作的:

  • 在0号格子读取了指令“在心里记一个数字1(记作i),记一个数字0(记作s)”,把头移到1号格子
  • 在1号格子读取了指令“如果 $i>10$ 就跳转到5号格子,否则跳转到2号格子”;目前 $i=1$,因此他把头移到2号格子
  • 在2号格子读取了指令“用 $s+i$ 的结果更新 $s$ ”,把头移到3号格子
  • 在3号格子读取了指令“用 $i+1$ 的结果更新 $i$ ”,把头移到4号格子
  • 在4号格子读取了指令“跳转到1号格子”,因此他把头移到1号格子
  • 在1号格子读取了指令“如果 $i>10$ 就跳转到5号格子,否则跳转到2号格子”;目前 $i=2$,因此他仍然把头移到2号格子
  • ……
  • 在1号格子读取了指令“如果 $i>10$ 就跳转到5号格子,否则跳转到2号格子”;终于 $i=11$,因此他跳转到5号格子
  • 在5号格子读取了指令“把s写到2000号格子里”,把头移到6号格子
  • 6号格子放着停机指令

在C语言中,存在 if 这样的分支结构,也存在 while 这样的循环结构,更有 goto 这样的任性的跳转。然而,在这位工人看来,这些全都是一种指令:分支指令。ifwhile在他眼里的区别仅仅是“是否重复执行”,ifgoto在他眼里的区别仅仅是“跳多远”而已。

相关文章