本文由 Claude Code 生成。

一、圖靈是什麼

1936 年,艾倫·圖靈(Alan Turing)提出了一個思想實驗:

如果一台機器能做到這三件事,它就能計算世界上任何可以被計算的問題。

這三件事是:

  1. 讀寫資料 — 能把東西存起來,也能讀出來
  2. 條件判斷 — 能根據結果決定下一步做什麼
  3. 重複執行 — 能一直跑,直到答案出現

這個概念叫做圖靈機,它不是一台真實的機器,而是一個理論模型,用來定義「什麼叫做計算」。

重要的是,圖靈說:計算的能力跟材料無關。 不管用真空管、電晶體、還是 CSS,只要能做到這三件事,就是一台電腦。這也是為什麼後來有人可以用 CSS 實作 x86 CPU——理論上,任何圖靈完備的系統都能模擬另一個圖靈完備的系統。

圖靈的貢獻是:他在電腦被造出來之前,就先用數學證明了「電腦能做什麼、不能做什麼」。現代所有電腦,本質上都是圖靈機的物理實現。


二、寫程式 === 紙打孔

在鍵盤出現之前,人類告訴電腦要做什麼的方式,是在卡片上打洞

打孔卡片的原理

1
2
有洞 = 1
沒洞 = 0

一張卡片有 80 欄 × 12 行,每個位置打洞或不打洞,組合起來就代表一條指令或一筆資料。

執行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
工程師想計算 3 + 5

在卡片上打出對應的洞(代表 MOV、ADD 等指令)

把一疊卡片放進讀卡機

讀卡機用光或電針掃描每個位置
有洞 → 通電 → 1
沒洞 → 不通電 → 0

0 和 1 的訊號送進記憶體

CPU 開始執行

結果從印表機印出來

打孔卡片 = 現代寫程式

這兩件事本質完全相同:

打孔卡片時代 現代
在卡片打洞 用鍵盤輸入程式碼
一張卡片 一個程式檔案
讀卡機 編譯器
一疊卡片的順序 程式的執行順序
洞的排列 = 機器碼 0 和 1 = 機器碼

差別只是輸入媒介。不管是打洞還是按鍵盤,最終進入 CPU 的都是一串 0 和 1。

打孔時代的日常困境

  • 沒有「編輯」這個概念:程式有 bug 要修改,必須重新打一張新卡片替換
  • 卡片順序很重要:一疊卡片掉在地上打散,程式就壞了,所以工程師會在卡片邊緣寫編號
  • 執行完才知道結果:提交一疊卡片後,可能等幾小時才拿到輸出,沒有即時回饋

這些限制,直接推動了磁帶、終端機、互動式程式設計的發明。


三、記憶體:從真空管到 IC

真空管時代(1940 年代)

真空管本質上是一個可以用電控制的開關

1
2
控制電壓低 → 開關關閉 → 0
控制電壓高 → 開關開啟 → 1

要用真空管存一個 bit,需要兩個真空管互相鎖住彼此的狀態,這個結構叫做 Flip-Flop(觸發器)

1
2
3
4
5
6
真空管 A 開著 → 讓 B 關著
真空管 B 關著 → 讓 A 繼續開著

這個狀態永遠維持,直到外力改變

這就儲存了一個 1

代價非常昂貴:

1
2
3
1 個 bit   = 2 個真空管
1 byte = 16 個真空管
1 KB = 16,384 個真空管

第一台電腦 ENIAC(1945 年)用了 17,468 個真空管,重 27 噸,佔地半個籃球場,耗電量足以點亮一個小鎮。而且真空管很容易燒壞,工程師每隔幾小時就要在幾千個管子裡找壞掉的那一個。

(補充:「Bug」這個詞的由來,正是因為有工程師真的在真空管裡找到一隻蟲子導致短路。)

電晶體時代(1950 年代)

電晶體做的事情跟真空管完全一樣——都是開關——但體積縮小了 100 倍,更穩定、更省電、更便宜。原理沒有改變,只是材料換了。

IC 積體電路(1960 年代至今)

IC 是把大量電晶體縮小並整合進一片晶片:

1
2
3
4
真空管時代:一個開關 = 燈泡大小的玻璃管
電晶體時代:一個開關 = 一粒米的大小
IC 時代: 一個開關 = 頭髮寬度的百萬分之一
現代晶片: 一片晶片 = 幾百億個開關

IC 沒有發明新東西,它只是讓同樣的事情變得更小、更多、更快。今天你的 CPU 裡有幾百億個電晶體,每一個都還是在做同一件事:開或關,1 或 0。

記憶體限制推動的發明

早期記憶體非常稀缺。IBM 360(1964 年的頂級機器)最多只有 8 MB,比現在的普通電腦少了 100 萬倍。

這個限制逼出了很多重要技術:

  • 虛擬記憶體:記憶體不夠用,就把硬碟當記憶體用
  • 壓縮演算法:資料縮小再存,節省空間
  • 高效排序演算法:在有限空間內做更多事

今天很多基礎演算法,都是被「記憶體不夠」這個問題逼出來的。


四、CPU 原理:以 8086 為例

CPU 的三個動作:Fetch → Decode → Execute

CPU 每一刻只做一件事,然後無限重複:

1
2
3
4
5
6
7
8
9
10
11
12
① Fetch(取得)
IP 暫存器指向記憶體中下一條指令的位址
CPU 把那個位址的內容取回來

② Decode(解碼)
CPU 查指令集對照表
10111000 是什麼意思?→ 這是 MOV AX

③ Execute(執行)
做對應的動作
IP 移到下一條指令
回到 ①

這個循環每秒重複幾十億次(現代 CPU 約 3GHz = 每秒 30 億次)。

8086 的暫存器

8086 CPU 有一組固定的「格子」,是 CPU 內建的臨時儲存空間,比記憶體快非常多:

通用暫存器(工作台)

名稱 用途
AX 算術結果、函式回傳值
CX 迴圈計數器
DX 乘除法輔助
BX 記憶體定址
SP 堆疊頂端位置
BP 堆疊框架基底
SI 字串來源位址
DI 字串目標位址

特殊暫存器

名稱 用途
IP 指向下一條要執行的指令位址
FLAGS 記錄上一次運算的狀態(結果是零嗎?有進位嗎?)
CS / DS / SS / ES 記憶體分段管理

指令集:CPU 的字典

指令集是 CPU 認識的所有指令的清單,由 Intel 設計時直接刻進電路裡。程式設計師和 Intel 之間的協議:Intel 保證這張表永遠不變,程式才能跨世代執行。

常見指令分幾類:

  • 搬資料MOV AX, 3 — 把 3 放進 AX
  • 算術ADDSUBMULDIV
  • 比較CMP AX, 0 — 比較 AX 和 0,結果存進 FLAGS
  • 跳轉JMP(無條件跳)、JZ(FLAGS 顯示結果為零才跳)
  • 堆疊PUSHPOP

一個完整的例子:計算 3 + 5

概念層(CPU 怎麼想)

  1. 把 3 放進暫存器
  2. 把 5 加到同一個暫存器
  3. 結果存在暫存器中(通常是 AX)

x86 組合語言

1
2
mov ax, 3    ; AX = 3
add ax, 5 ; AX = AX + 5 = 8

CPU 一步一步執行:

1
2
3
4
5
6
7
8
9
10
11
時脈第 1 下:
Fetch → 取出 MOV 指令
Decode → 這是 MOV,目標是 AX,值是 3
Execute → AX = 3,IP 移到下一條

時脈第 2 下:
Fetch → 取出 ADD 指令
Decode → 這是 ADD,目標是 AX,加上 5
Execute → AX = 3 + 5 = 8,IP 移到下一條

結果:AX = 8

組合語言最終會被組譯器翻譯成機器碼(一串 0 和 1),CPU 實際執行的是那串數字。指令集就是這張翻譯表,由 Intel 設計時直接刻進電路裡——前幾個 bit 代表「做什麼」,後幾個 bit 代表「對誰做」,沒有數學道理,就是規定。

整條鏈

從打孔卡片到現代,這條鏈從來沒有改變:

1
2
3
4
5
6
7
8
人的想法
↓ 高階語言(Python / C)表達
↓ 編譯器翻譯成組合語言
↓ 組譯器查指令集對照表,輸出機器碼
↓ 機器碼載入記憶體
↓ CPU 不斷 Fetch → Decode → Execute
↓ 電晶體開關切換(0 和 1)
↓ 結果

每一層都是下一層的包裝。最底層永遠只有一件事:開關是開的還是關的。


這就是電腦的全部。從圖靈 1936 年的理論,到打孔卡片,到真空管,到 IC,到今天幾百億顆電晶體的晶片——做的都是同一件事,只是越來越小、越來越快。