我努力地呼吸
I try hard to breathe
仍然覺得氧氣不夠
Maybe the oxygen is too thin and
肺部仿佛被什麼東西重重壓著
Something heavy presses my lung
快要令我窒息
Which make me suffocating
《Go 設計與實現》筆記之第二章 編譯原理
編譯原理這一章講 Go 的源代碼是如何變成二進制碼的,可分爲四個過程:
- 詞法和語法分析 lexical and grammar analysis
- 類型檢查 type check
- 中間代碼生成 IR(intermediate representation) generation
- 機器碼生成 machine code generation
詞法分析是將源代碼視爲字符串序列,對各個字符串進行標記,生成 Token 序列。
語法分析是將 Token 序列按照 LALR(1)(向前查看和自底向上解析)的解析文法進行解析,生成一棵AST (抽象語法樹 )。
類型檢查分靜態類型檢查和動態類型檢查,靜態類型檢查會在編譯期對變量賦值、返回值和函數參數進行類型檢查;動態類型檢查在代碼運行時進行,可以實現向下類型轉換、延遲綁定和反射等功能。
中間代碼生成階段首先會進行 ssaconfig 的初始化,緩存可能需要用到的類型和指針;然後進行遍歷和替換,將內建函數 make、map、channel、new、select、panic、recover 等等替換成 runtime 包的函數。最後不斷地進行中間代碼生成,優化代碼,生成類似彙編代碼的代碼。
機器碼生成階段分兩個部分:一是 SSA 中間代碼降級、針對特定CPU架構的中間代碼優化和重寫,最後生成相當接近特定架構彙編代碼的指令;二是將特定架構的指令轉成二進制代碼。
《Go 設計與實現》筆記之第一章 準備工作
深入學習 Go 語言的設計與實現之前要準備以下工作:
- 克隆 Go 倉庫源代碼並編譯它。
- 了解 Plan 9 彙編,知道 Go 的棧結構並能分析代碼的執行過程。
彙編者,二進制代碼的文本形式也,其最大的特點就是不可移植。Plan 9 彙編是貝爾實驗室的九號計劃的產物,目前被用於 Go 程序編譯的中間代碼,因爲 Go 的作者 Rob Pike,同時也是 Plan 9 彙編的作者。
Plan 9 彙編指令與 Intel 等彙編等的不同在於:
一般情況下,命令的源操作數在先,目的操作數在後。 如同樣是將十六進制的 10 傳送到 AX寄存器,在 Plan 9 中是
MOVQ $0x10, AX
,而在 Intel 彙編中是mov rax, 0x10
。棧的調整通過硬件 SP 寄存器進行加減運算實現。而 Intel 彙編中通過 push 和 pop 命令實現。
操作的數據長度取決於命令的後綴。而 Intel 彙編取決於寄存器。
// plan 9 彙編
MOVB $1, DI // 1 byte
MOVW $0x10, BX // 2 bytes
MOVD $1, DX // 4 bytes
MOVQ $-10, AX // 8 bytes
// intel 彙編
mov rax, 0x1 // 8 bytes
mov eax, 0x100 // 4 bytes
mov ax, 0x22 // 2 bytes
mov ah, 0x33 // 1 byte
mov al, 0x44 // 1 byte
通過分析 Plan 9 彙編代碼我們可以繪製出如下的棧結構:
十二生肖、干支紀年法與Go語言編程
如無特殊說明,本文標音採用甲子話拼音方案。甲子話系陸豐市甲子鎮通行的語言,屬閩南語潮汕話三甲片。
十二生肖
十二生肖本地讀音爲:
- 鼠牛虎兔 /cu² ngu⁵ hao² tao³/
- 龍蛇馬羊 /lêng⁵ zua⁵ bhê² ion⁵/
- 猴雞狗豬 /gao⁵ goi¹ gao² du¹/
干支紀年法
「干」是天干,有 10:
- 甲乙丙丁 /gah⁴ ig⁴ bian² dêng¹/
- 戊己庚辛 /bhao⁷ gi² gên¹ sing¹/
- 壬癸 /rim⁶ gui³/
「支」是地支,有 12:
- 子丑寅卯 /zu² tiu² ing⁵ bhao²/
- 辰巳午未 /sing⁵ zi⁶ ngao² bhi⁷/
- 申酉戌亥 /sing¹ iu² sug⁴ hai⁶/
天干從甲開始,地支從子開始,天干地支相配形成 60 種組合,用來紀年。從甲子出發,60 年後又回到甲子,因此稱 60 年爲「一甲子」。
十二地支與十二生肖相對應,因此也用生肖紀年。如甲子年,地支爲「子」,對應生肖「鼠」,因此甲子年也稱之為「鼠年」。
問題
問題1:已知 2020 年是鼠年,請問 2021 年是什麼年?
排在鼠之後的生肖是牛,因此 2021 年是牛年。
問題2:已知 2020 年是庚子年 /gên¹ zu² ni⁵/,請問 2021 年是什麼年?
庚之後爲辛,子之後爲丑,因此 2021 年是辛丑年 /sing¹ tiu² ni⁵/。
問題3:已知 1024 年是甲子年,問最近過去的甲子年和將要到來的甲子年是公元多少年?
Go 基礎
Go 又稱 Golang,是 Google 公司 2009 年發佈的一款開源編程語言,以簡潔而高效的代碼著稱。Go 目前使用 GOPATH 或 Go modules 管理項目中的第三方依賴,且不支持循環依賴。
Go 的基本數據類型有 16 種(不包含別名),可謂豐富。要注意的是 string 也是基本數據類型,表示一個 UTF-8 字符串,底層是 byte 數組。單個字符使用一個 int32 的別名 rune 表示,其值爲 Unicode 字符碼點。
Go 的變量使用 var 關鍵字聲明,也可以使用短變量聲明;常量則是使用 const 關鍵字聲明。
Go 有 if 條件語句、for 循環語句、switch 選擇語句和 defer 延遲執行語句。Go 的 for 語句兼具其他語言的 while 語句;switch 語句每個 case 條件都支持運算,且默認自動 break;defer 語句可以立即計算參數值,然後在函數 return 前執行。
Go 的可見性跟標識符的首字母是否大寫相關,大寫則是導出的,包外可見;小寫則是非導出的,包外不可見。
Go 有指針,要注意 &
是生成指向操作數的指針,而 *
是獲取指針指向的底層值。
Go 將一些字段組合成一個結構體(struct) 。結構體支持隱式間接調用,即 p.X 等價於 (*p).X(p 爲結構體指針)。
Go 支持數組,更重要的是支持一種名爲切片(slice) 的動態數組。切片是一個數組片段的描述,包含指向數組的指針、片段的長度和容量。
Go 支持映射(map)。
Go 支持函數(function)的命名返回值、多值返回、作爲值傳遞和閉包。還有一種稱爲方法(method)的特殊函數,其帶有接收者參數,接收者可以是值接收者也可以是指針接收者,指針接收者可以更改接收者的值。
Go 支持將一些方法簽名組成接口(interface)。接口的實現是隱式的,不需要「implements」。一個結構體作爲接收者定義了接口中的所有方法,那麼該結構體就是實現了該接口。因爲隱式的緣故,建議接口方法盡可能少,以便管理。接口是值,可以傳遞。底層值是 nil 的接口,其方法可以被調用;接口本身爲 nil ,則意味著其不保存值也不保存具體類型;空接口(interface{})是包含零個函數的接口,不是爲 nil 接口。
Go 的異常處理很簡單,只有 Error,沒有 throws。
Go 支持 Go 程,一種 Go運行時管理的輕量級線程。Go 程之間可以通過信道(channel) 通信,通過 Mutex 或 RWMutex 共享互斥變量,通過 WaitGroup 等待執行完成。
Go 中的信道還支持通過 for-range 循環讀取數據,當信道關閉時該循環自動退出。記住,只有發送方可以關閉信道,接收方不能。信道還支持 select 語句,其會阻塞到某一分支可以執行爲止,如沒有分支可以執行且設定了default 語句,會一直執行 default 語句。