跳轉到

9.1 靠近硬體的抽象:指標與記憶體地址的精準操控#

C 的誕生現場:貝爾實驗室的 1969–1973#

故事從 UNIX 開始。Ken Thompson 1969 年在貝爾實驗室的廢棄 PDP-7 上(實驗室不給他買機器跑自己的遊戲 Space Travel——微觀政治與閒置資源催生大發明)寫出了 UNIX 雛形,用 B 語言。1971 年實驗室購入 PDP-11,移植需求暴露了 B 的短板:無型別、字組長度固定。

Dennis Ritchie(1941–2011)在 1971–1973 年間把 B 改造成 NB(New B)再改名 C:回歸型別(字元、整數、浮點、陣列、結構),並把 BCPL/B 的「向量」升級為指標 + 位址算術。1973 年,UNIX 核心用 C 重寫——作業系統第一次用高階語言寫成(此前系統軟體都是組語的禁臠)。這是一場靜默革命:它證明高階語言可以進入系統層,從此「語言」與「硬體」之間不再有不可逾越的組語護城河。

指標:C 的靈魂與詛咒#

指標的本質一句話:變數的值是另一個變數的位址。

int x = 42;
int *p = &x;      /* p 存放 x 的位址 */
*p = 7;           /* 透過位址改寫 x */

指標使 C 獲得了三種力量:

  1. 資料結構的自由:鏈表、樹、圖——動態結構全靠指標穿針引線
  2. 傳址語義:函數可以修改呼叫者的變數
  3. 零成本的抽象:陣列 a[i] 編譯為 *(a + i)——語法糖之下就是位址算術

「指標即陣列即位址」的統一,讓 C 成為記憶體的透明視窗:你能寫的每一行代碼,都能在心裡展開為機器將要做的每一件事。這種「無欺騙性」是 C 哲學的核心(9.3)。

位址算術的優雅與深淵#

C 的指標支援算術:p + 1 前進一個元素(不是一個位元組——編譯器按型別大小縮放)。但同一機制在無人看管時就是深淵:

災難模式 後果
越界存取(buffer overflow) 1988 年 Morris 蠕蟲藉 fingerd 的溢出感染全球 6 千台機器,第一起網路安全事件
懸空指標(use-after-free) 漏洞利用的頭號素材
空指標解引用 程式崩潰(segfault)
型別混淆 未定義行為的迷宮

未定義行為(UB)是 C 標準的深層設計:標準不規定「錯誤的程式」會發生什麼——把檢查成本從編譯器移給程式員。在 1970s 這是合理的經濟學(每次越界檢查都是寶貴的機器週期);在 2020s 它是安全災難的溫床(主流漏洞清單中記憶體安全類長期過半,微軟 2019、Google 2020 的統計都指向 70% 級別)。一個時代的合理妥協,成為另一個時代的系統性風險——這是技術債的教科書定義。

指標的教育史:入門之牆#

「指標」幾十年來是程式教學的第一道鬼門關。兩種教學哲學:

  • C 先行派:先受苦再抽象(K&R《C 程式設計語言》1978 的傳統,該書以「薄、準、美」著稱)
  • 抽象先行派:先用 Python/Scratch 建立信心,指標延後(2000s 起的主流)

歷史曲線有趣:2000s 抽象派獲勝,但 2010s 安全危機又把指標(的觀念)請回課堂——Rust 甚至用型別系統來教指標語義(借用檢查器是「指標的教育學」的最高形態,15.2)。

指標的現代變奏#

  • 引用(reference):C++/Java/Python 的受控指標——把危險面封裝起來
  • 智慧指標:C++ 的 unique_ptr/shared_ptr——用 RAII(11.2)自動管理
  • 借用與所有權:Rust 把指標規則寫進型別系統——編譯器成為指標紀律的執法者
  • 理論本源:程式的記憶體模型 = 有向圖,「指標 = 圖的邊」;GC(7.3)= 可達性

小結#

C 用「指標 + 型別」在 1972 年畫出了一條至今有效的線:

距離硬體夠近(可寫作業系統),距離人類夠遠(不必寫組語)。

下一節看 C 與 UNIX 的共生如何塑造了整個軟體生態。


下一節:9.2 UNIX 與 C 的共生