敲几行代码,电脑凭什么就『算』出来了?
你敲下一行 print("hello"),回车,屏幕上出现了文字。这中间发生了什么?这是这门《计算机科学导论》第一章最想让你回答的问题,也是我们整篇文章要爬的阶梯:从"计算"到底什么意思,一路到一台台机器连成互联网。
答案不是一个单个的事实,而是一条抽象链。我们一节一节往上爬。
不造机器之前,先问『计算』到底是什么意思
先问一个乍看多余、其实关键的问题:计算(computation)到底是什么?
大多数人的第一反应是"算数":加法、乘法、解方程。但如果计算机科学只研究算数,它就不配叫一门学科了。课件的定义是:一个计算,指任何定义良好、能机械执行的变换。它可能是算术的(求和),也可能是非算术的(排序、查找、翻译)。换句话说,"算"不只是算出数字,也包括把一堆数据变成另一堆数据。
看下面这张图,感受"计算 = 输入 → 过程 → 输出"这套骨架:

图注:同一个输入数组,喂给"求和(Sum)"这个算术变换,得到数字 108;喂给"降序排序(Sort)"这个非算术变换,得到排好序的另一串数。箭头中间那个方框就是"计算",它把输入变成输出。右侧的算盘提醒我们,机械计算在人类历史里早已存在。
注意第二行:输出的不是数字,而是一串重新排好的序列。这说明"会算"不一定等于"会算数",这套输入—输出骨架才是理解一切程序的地基。
那为什么拖到 1930 年代,大家才发现"计算"这个词本身需要被定义?因为在图灵之前,"能算"是个模糊的日常概念,没人能回答"什么东西能算、什么东西永远算不出来"。图灵在 1936 年的论文里第一次把"可计算"钉成严格的技术概念:一个数是可计算的,当且仅当能由一台机器把它的小数一位位写出来。Turing 1936
至于人算用的算盘、19 世纪雅卡尔织机用穿孔卡片控制图案、ENIAC 用一块块接线板和开关来"编写程序"——这些早期装置共同的故事线是:程序与数据可以装在外部的物理载体上,还没进到"存进机器内存"这一步。Computer History
这样,问题就准备好了:到底该造一台什么样的"机器",才能机械地执行任何定义良好的变换? 于是我们看向图灵。
用一台概念机钉死『所有能算的』—— 图灵机和那条能力分界线
图灵的答案不是一台贴着型号的真实电脑,而是一台概念机器。它简单到不可思议,却有本事覆盖"所有能算的事"。它由三样东西构成,图看得最清楚:

图注:图灵机的"三件套":上半部分是一排可无限延长的纸带格子(对应现代机器的内存/存储器),一个读写头悬在某一格上(对应CPU)只看这一格,旁边挂着内部当前状态和一张规则表(对应程序)。下半部分是它一次一步的动作:读当前格符号 → 按规则改写 → 左/右移一格 → 转入新状态;找到不匹配的规则就停机。每步它都只"照顾"所在的那一格。
纸带是无穷长的,每格装一个符号(往往是 0 或 1);读写头一次只看一格,能读写、能左右移动;那张规则表告诉它"You 现在处于状态 A、读到了 1,就把这个格子改写为 0、把纸带右移一格、改到状态 B"。这样,一台图灵机每步做"读、改、移、转态",重复到规则表里没有匹配项就停。Turing machine
现在你可以回答上一章的问法:这台机器能机械地执行"计算"吗?能,只要你把要算的东西先写成纸带上的符号,再给它一张写明步骤的规则表。图灵用一个干脆的定义收口:一个函数是图灵可计算(Turing computable)的,当且仅当它的值能用某台图灵机求出来——也就是喂进输入、有限步内得出答案。
这时候一个奇妙的飞跃出现了:规则表本身也可以被写成纸带上的符号。图灵证明,存在一台"通用图灵机(Universal Turing Machine)",你把另一台机器的规则表放在它纸带开头,它就能读出这张表、模拟那台机器干活。用大白话说,程序第一次变成了"可以被另一台机器读进嘴里的数据"。Turing 1936
这台通用图灵机带来一个"能力分界线"的概念,叫图灵完备(Turing complete):一个系统只要能用来模拟任意一台(通用)图灵机,给定足够的时间和内存能执行任意计算,那它就是图灵完备的。按这个严格定义,几乎所有现代编程语言(Python、Java、C++)、你桌上的笔记本手机,都是图灵完备的。Turing completeness
课件还列了一串"令人意外"的例子:PowerPoint 动画、Minecraft 的红石电路、康威的生命游戏。要澄清的是,这些多为有公开演示、需要带限定语的说法,不是这节课的判断核心;对初学者只要抓住一句话:图灵完备讲的是"能力上能跑任意计算",跟跑得快不快、会不会"自动理解一切"是两回事。
故事讲到这里,规则表已经住进了纸带、程序变成了数据。下一章我们转向真实机器:既然"程序是内存里的一堆符号",那现代计算机为什么不照图灵的样子造?
内存里装的到底是程序还是数据?(两种都装——这就是存储程序)
图灵的通用机让"程序变数据"成为理论事实,但它仍是一台想象的机器。真正把这个思想落实成硬件蓝图的是冯·诺依曼等人,落实的结果就叫存储程序(stored-program)。
关键在于一句话:一台存储程序计算机,用同一种底层机制来编码程序指令和数据,两者住在同一块可以被寻址的内存里。数据可以被 CPU 读进、改出;程序这条"规则表"同样住在内存里,被 CPU 一条一条取出来执行。Von Neumann 架构
这节课正中央有一个小测验:"根据冯·诺依曼模型,内存中存放的是什么?A. 只有数据 B. 只有程序 C. 数据与程序都有 D. 都没有。" 你现在应该能选 C:两种都装。程序和数据共用同一块可寻址内存,只是 CPU 逐条取出来的是指令、被操作的是数据。
那张著名的冯·诺依曼五部件框图,能把这套话说清楚:

图注:倒 U 形的输入/输出接口层包住了三个核心组件:算术逻辑单元 ALU(做算术运算)、控制单元 CU(负责取指和指挥)、以及 Storage 存储(存指令和数据)。左侧 Input Data 流入、右侧 Output Data 流出。现代 CPU 正是把这套蓝图工程化了——ALU 和控制单元被封装进芯片,Storage 则对应内存和更快的缓存。
把三个部件对齐到常识:ALU 负责"算",控制单元负责"什么时候取哪条指令来执行",Storage 就是存放程序代码 + 数据的内存。整台机器的动作是——控制单元从内存取出一条指令 → 交给 ALU 执行 → 再取下一条。课件里有两层提醒要记住:一是数据和程序在内存里用的是同一个逻辑格式(都是一串 0 和 1);二是程序包含有限条指令,并且一次执行一条(one at a time)。
要补一段诚实的历史署名的提醒:这套"程序与数据同住一块可寻址内存"的蓝图,常被记在冯·诺依曼名下——他在 1945 年写成著名的 First Draft of a Report on the EDVAC 把它综合为一份流传很广的报告;但严格说,此前 Eckert 与 Mauchly 在造 ENIAC/EDVAC 时已基本完成这一设计。准确的说法是:冯·诺依曼把这套已存在多月的思想整理、撰写成了蓝图,而不是一个人凭空发明了它。Computer History
蓝图有了,现在追问一件俗但重要的事:蓝图里那个"Storage",真机上到底是什么样子、有多大、有多快?
蓝图里的那个『内存』,在真机上原来是一整座金字塔
上一章那个笼统的"Storage",落到真实硬件上根本不是一个东西,而是一整座越往外越大的金字塔:

图注:六级存储金字塔。从顶端的寄存器(Register)往下是 CPU 内核里的 L1、内核外的 L2 缓存(cache)、主内存 DRAM、本地磁盘、最底部的远程存储。左侧箭头标注越往下"容量与尺寸"越大,右侧箭头标注越往上"速度与价格"越高。一句话记忆:离 CPU 越近的越快、越贵、也越小;离得越远越慢、越便宜、也越大。
为什么不一律用最快的存储?因为最快的那层贵得离谱、容量又小。设计者的办法是分层折中:CPU 先到最近的寄存器找数据,找不到再掉头去 L1、L2 缓存、主存 DRAM……逐级往下挖。一台 2020 年代的台式机告诉我们量级差得多惊人:寄存器延迟约 0.25 纳秒,主内存 DRAM 约 82.5 纳秒,固态硬盘 SSD 约 0.2 毫秒,机械硬盘则到几毫秒——每往下一层,慢好几个数量级、但同样容量便宜得多。存储层次
这一章还有一句贯穿全篇的总纲:计算机里一切都是二进制。寄存器、缓存、内存、磁盘里躺着的,最终都是 0 和 1——程序也好、数据也罢,都落在这座金字塔的某一层上,以二进制的方式存放。这里有个单位换算很常用:1 字节(B)= 8 位(bit),1024 字节 = 1 KB,逐级到 MB、GB、TB、PB。先记住"一个字节 = 8 位"这个小事实,第七章它会在 IP 地址那里再次兑现。
人写的语言,怎么变成机器一格格跑起来的动作?——编译与解释
金字塔讲的是机器"下层"物理怎么存储;现在把视线拉高,看"上层"的人怎么跟机器对话。
机器只认得最底层的 0/1(也就是机器语言 machine code),人写的高级语言显然不在这层。之间隔着一层层的抽象:机器语言在最底,汇编语言居中,Python、C++、Java、C 这类高级语言在最上面,再往上才是接近人话的自然语言。编译器
要把高级语言变成机器能跑的东西,有两条路,对应两张流程:

图注:编译型工作流(对应 C/C++、Java、Rust、Go 等语言):整份源码(Source Code,文本格式)先交给编译器,在执行前整个翻译成可执行文件(Object/Executable File,标注 Reusable 可复用),再由 CPU 去执行。好处是运行快,代价是:每次改了一行代码,都要重新编译一遍才能再跑。
课件把另一条路径画成了并排的姐妹图:解释型路径。同样的源码直接交给一个"解释器(interpreter)",它逐行读、逐行执行,不事先转为机器码。Python、JavaScript、Ruby、PHP 常走这条路。它的好处是改一行立刻生效、方便边写边试,代价是解释执行通常比先编译好的原生机器码慢一些。编译器
别把"C 一定编译、Python 一定解释"背成铁律——这只是实践中各语言通常采用的一种习惯归类,理论上同一门语言既可编译也可解释。
编译这条路有个关键点,画出的可执行文件并不是终点:它只是把"人写的源码"翻译成了机器认识的形态,真正放它进内存、逐条开跑,还要等下一章的"幕后管家"来接手。
编译好的程序不是自己会跑——谁在后台把文件搬进内存又逐条执行?
你双击一个 .exe,文件里的指令不会自己跑起来。在 CPU 逐条执行之前,必须有一个"管家"先做一堆看不见的工作,这个管家就是操作系统(Operating System)。
课件给操作系统一句话定义:它是系统软件(system software),管理者计算机的硬件和软件资源,并为各个计算机程序提供公共服务。翻译成人话:它是夹在"你的应用"和"底层硬件"之间那一层,帮程序分配内存、分配 CPU 时间,还替不懂硬件的程序员把一些琐碎细节藏起来。这四类职责分别可以理解为:分资源、让程序互相隔离又允许它们通信、给程序一个比真实硬件更"好用"(甚至虚拟)的抽象、以及提供访问网络和磁盘等设备的通用接口。操作系统
双击一个可执行文件背后到底发生了什么?课件给了这样一张完整的时序图:

图注:编号 1–7:① 你双击 .exe,向操作系统发出"信号";② 操作系统从文件系统读取这个文件的内容;③ 读回;④ 操作系统分配一段内存空间,把程序代码和数据写进内存;⑤(CPU)运行这个程序;⑥ 把执行结果送回显示;⑦ 改变屏幕显示。整个过程里,程序自己不会跑,是操作系统在后台负责装载和分配。
顺着这张图走,你就把第四章那座金字塔和第三章的存储程序连起来了:磁盘上待命的可执行文件(程序)被 OS 读到,"落"进金字塔的主存那一层,才轮到 CPU 逐条取指执行。反过来,你点窗口右上角那个 X 关闭程序时,OS 会接着做"清理此前分配给这个程序的内存"这件隐形家务。这两则日常动作,把"OS 是幕后管家"这条抽象职责变成了看得见的因果。操作系统
顺带解决课程里那道软件分类小测:为什么编译器、操作系统都归系统软件? 系统软件的分界标准不是"重不重要",而是"它帮谁干活"。操作系统管资源、编译器把代码变成可执行程序、系统工具(防火墙、磁盘分区)维护机器,三者都不直接面向终端用户提供某个业务,而是为了让别的程序(应用)能跑起来。应用软件才是真正对着用户的:通用型的微信、Word、Excel、游戏、浏览器,和专用型的图书管理系统、工业控制系统等等。这个画家分清楚,软件分类题型就不怕了。
现在,这台能"算"的机器已经能装程序、跑程序了。但世界不可能一台机器孤零零地算——我们上一章口口声声说"操作系统在磁盘/内存间搬运",一旦你的程序要访问的是另一台机器上的服务呢?
从敲下网址到页面打开中间发生了什么?——把一台机器连成互联网
最后一格阶梯,是把无数台"冯·诺依曼机 + 操作系统"连成一张网。
先说两套常让学生犯晕的"刻度"。要给互联网上的两台机器传一句话,事情复杂到必须分层来做。两套常见的分法:教科书爱用的 OSI 七层模型(物理层、数据链路层、网络层、传输层……直到应用层),以及真实互联网实际跑的 TCP/IP 四层模型。它们只是描述同一件事的两把尺子,不必逐层背下七层的职能表。OSI 模型
关键的动作是封装与解封装:在发送端,数据从最高层一路往下走,每进入下一层,就给数据再套上一个"头"(这层的标识信息,比如到哪一层目的地址);到了电线对侧的接收端,再从最底层一层层把各自的头脱下来往上交。OSI 模型
分层解决了"这么多层谁负责什么",可还有一个根本问题:这么多台机器,我怎么准确找到我要找的那一台? 这就轮到 IP 地址登场。
互联网给每台上网设备发一个"网络层门牌号",就是IP 地址。这里兑现我们第四章埋的字节伏笔——看这张图:

图注:一个 IPv4 地址 192.168.43.241 由四段(octet,八位组)组成,每段落到底下正好是 8 位二进制(8 Bits = 1 Byte,一个字节),四段加起来就是 32 位 = 4 字节。这正是"一切皆二进制"在网络地址上的又一次兑现。
IPv4 就是那个 32 位数字、能提供约 $2^{32}$(约 43 亿)个地址。但这套设计诞生于上网者寥寥的 1980 年代,随着上网人口和手机爆发式增长,43 亿很快不够分。缓解手段(CIDR 细分、NAT 让一个私网共享一个出口 IP)只是打补丁。于是有了下一代IPv6:地址从 32 位扩到 128 位,可用的地址空间高达 $2^{128} \approx 3.4 \times 10^{38}$ 个,据估计足以给地球上每一粒沙子都分配地址。全球 IPv4 顶层地址池在 2011 年宣布用尽,各地区的分配机构随后相继告罄——这是"为什么要 IPv6"最直接的原因。IPv4 耗尽
好,现在机器能用 32 位(或 128 位)号码互相找到对方了。可问题来了:你记不住 142.250.72.14,你只记得 google.com。谁来把"人记得的名字"翻成"机器用的号码"?
答案是 DNS(域名系统)——官方科普给它起了一个绝妙的俗称:互联网的电话簿。DNS
看课件这张最小闭环图:

图注:你在浏览器敲下域名后发生的事:先向 DNS 服务器发出域名查询(Enquiry),DNS 服务器把该域名对应的 IP 地址返回给你(图中以 xxx.xxx.xxx.xx 示意),浏览器再用这个 IP 去访问网站(这里是 google)。真实流程在中段要多走几站:由"递归解析器"出发,经根服务器、顶级域服务器(.com)一路"指路",最后由真正握有记录的"权威服务器"把最终 IP 交还回来。
把最后一句话连起来,就是整章、也是整篇文章那个闭环:你敲下 www.google.com → 系统先去"电话簿"DNS 那里把域名翻译成 IP → 浏览器拿着 IP,按我们前面学的分层封装好数据、沿网线发出去 → 对方那台机器靠 IP 找到 → 网页按同样路径传回来显示在你屏幕上。
你看,爬完这六格阶梯,那个最初的问题——"我只敲了几行代码,凭什么就『算』出来了"——已经有了完整的答案:内存里躺着程序和数据(存储程序),CPU 逐条取上来执行;机器只认 0/1,人写的语言要经编译器或解释器翻成机器码,再由操作系统这位管家装进内存、逐条调度;当程序想找另一台机器时,它们靠唯一 IP 门牌、靠 DNS 电话簿这类机制在互联网上握手。你敲下的每一行代码、每次点开的网页,都是这条抽象链上一环一环接力跑出来的。
参考文献
本专题引用的核心资料如下,建议深入学习时阅读原文:
- Turing machine — Wikipedia — https://en.wikipedia.org/wiki/Turing_machine
- Turing completeness — Wikipedia — https://en.wikipedia.org/wiki/Turing_completeness
- Turing, A.M. "On Computable Numbers, with an Application to the Entscheidungsproblem" (1936/37; PLMS 42:230–265) — https://www.cs.virginia.edu/~robins/Turing_Paper_1936.pdf
- Von Neumann architecture — Wikipedia — https://en.wikipedia.org/wiki/Von_Neumann_architecture
- Memory hierarchy — Wikipedia — https://en.wikipedia.org/wiki/Memory_hierarchy
- Compiler — Wikipedia — https://en.wikipedia.org/wiki/Compiler
- Operating system — Wikipedia — https://en.wikipedia.org/wiki/Operating_system
- OSI model — Wikipedia — https://en.wikipedia.org/wiki/OSI_model
- IPv4 address exhaustion — Wikipedia — https://en.wikipedia.org/wiki/IPv4_address_exhaustion
- What is DNS? — Cloudflare Learning Center — https://www.cloudflare.com/learning/dns/what-is-dns/
- Computer History Museum · Timeline of Computer History (computers) — https://www.computerhistory.org/timeline/computers/