# 第十三章 底層編程
Go語言的設計包含了諸多安全策略,限制了可能導致程序運行出錯的用法。編譯時類型檢查可以發現大多數類型不匹配的操作,例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型,都有嚴格的類型轉換規則。
對于無法靜態檢測到的錯誤,例如數組訪問越界或使用空指針,運行時動態檢測可以保證程序在遇到問題的時候立即終止并打印相關的錯誤信息。自動內存管理(垃圾內存自動回收)可以消除大部分野指針和內存泄漏相關的問題。
Go語言的實現刻意隱藏了很多底層細節。我們無法知道一個結構體真實的內存布局,也無法獲取一個運行時函數對應的機器碼,也無法知道當前的goroutine是運行在哪個操作系統線程之上。事實上,Go語言的調度器會自己決定是否需要將某個goroutine從一個操作系統線程轉移到另一個操作系統線程。一個指向變量的指針也并沒有展示變量真實的地址。因為垃圾回收器可能會根據需要移動變量的內存位置,當然變量對應的地址也會被自動更新。
總的來說,Go語言的這些特性使得Go程序相比較低級的C語言來說更容易預測和理解,程序也不容易崩潰。通過隱藏底層的實現細節,也使得Go語言編寫的程序具有高度的可移植性,因為語言的語義在很大程度上是獨立于任何編譯器實現、操作系統和CPU系統結構的(當然也不是完全絕對獨立:例如int等類型就依賴于CPU機器字的大小,某些表達式求值的具體順序,還有編譯器實現的一些額外的限制等)。
有時候我們可能會放棄使用部分語言特性而優先選擇具有更好性能的方法,例如需要與其他語言編寫的庫進行互操作,或者用純Go語言無法實現的某些函數。
在本章,我們將展示如何使用unsafe包來擺脫Go語言規則帶來的限制,講述如何創建C語言函數庫的綁定,以及如何進行系統調用。
本章提供的方法不應該輕易使用(譯注:屬于黑魔法,雖然功能很強大,但是也容易誤傷到自己)。如果沒有處理好細節,它們可能導致各種不可預測的并且隱晦的錯誤,甚至連有經驗的C語言程序員也無法理解這些錯誤。使用unsafe包的同時也放棄了Go語言保證與未來版本的兼容性的承諾,因為它必然會有意無意中使用很多非公開的實現細節,而這些實現的細節在未來的Go語言中很可能會被改變。
要注意的是,unsafe包是一個采用特殊方式實現的包。雖然它可以和普通包一樣的導入和使用,但它實際上是由編譯器實現的。它提供了一些訪問語言內部特性的方法,特別是內存布局相關的細節。將這些特性封裝到一個獨立的包中,是為在極少數情況下需要使用的時候,同時引起人們的注意(譯注:因為看包的名字就知道使用unsafe包是不安全的)。此外,有一些環境因為安全的因素可能限制這個包的使用。
不過unsafe包被廣泛地用于比較低級的包, 例如runtime、os、syscall還有net包等,因為它們需要和操作系統密切配合,但是對于普通的程序一般是不需要使用unsafe包的。
- 前言
- Go語言起源
- Go語言項目
- 本書的組織
- 更多的信息
- 致謝
- 入門
- Hello, World
- 命令行參數
- 查找重復的行
- GIF動畫
- 獲取URL
- 并發獲取多個URL
- Web服務
- 本章要點
- 程序結構
- 命名
- 聲明
- 變量
- 賦值
- 類型
- 包和文件
- 作用域
- 基礎數據類型
- 整型
- 浮點數
- 復數
- 布爾型
- 字符串
- 常量
- 復合數據類型
- 數組
- Slice
- Map
- 結構體
- JSON
- 文本和HTML模板
- 函數
- 函數聲明
- 遞歸
- 多返回值
- 錯誤
- 函數值
- 匿名函數
- 可變參數
- Deferred函數
- Panic異常
- Recover捕獲異常
- 方法
- 方法聲明
- 基于指針對象的方法
- 通過嵌入結構體來擴展類型
- 方法值和方法表達式
- 示例: Bit數組
- 封裝
- 接口
- 接口是合約
- 接口類型
- 實現接口的條件
- flag.Value接口
- 接口值
- sort.Interface接口
- http.Handler接口
- error接口
- 示例: 表達式求值
- 類型斷言
- 基于類型斷言識別錯誤類型
- 通過類型斷言查詢接口
- 類型分支
- 示例: 基于標記的XML解碼
- 補充幾點
- Goroutines和Channels
- Goroutines
- 示例: 并發的Clock服務
- 示例: 并發的Echo服務
- Channels
- 并發的循環
- 示例: 并發的Web爬蟲
- 基于select的多路復用
- 并發的退出
- 示例: 聊天服務
- 基于共享變量的并發
- 競爭條件
- sync.Mutex互斥鎖
- sync.RWMutex讀寫鎖
- 內存同步
- 競爭條件檢測
- 示例: 并發的非阻塞緩存
- Goroutines和線程
- 包和工具
- 包簡介
- 導入路徑
- 包聲明
- 導入聲明
- 包的匿名導入
- 包和命名
- 工具
- 測試
- go test
- 測試函數
- 測試覆蓋率
- 基準測試
- 剖析
- 示例函數
- 反射
- 為何需要反射?
- reflect.Type和reflect.Value
- Display遞歸打印
- 示例: 編碼S表達式
- 通過reflect.Value修改值
- 示例: 解碼S表達式
- 顯示一個類型的方法集
- 幾點忠告
- 底層編程
- unsafe.Sizeof, Alignof 和 Offsetof
- unsafe.Pointer
- 示例: 深度相等判斷
- 通過cgo調用C代碼
- 幾點忠告
- 附錄
- 附錄A:原文勘誤
- 附錄B:作者譯者
- 附錄C:譯文授權
- 附錄D:其它語言