diff --git a/ch13/ch13-02.md b/ch13/ch13-02.md index fc9fd2e..b3e9cfd 100644 --- a/ch13/ch13-02.md +++ b/ch13/ch13-02.md @@ -18,9 +18,8 @@ fmt.Printf("%#016x\n", Float64bits(1.0)) // "0x3ff0000000000000" 許多將unsafe.Pointer指針轉爲原生數字,然後再轉迴爲unsafe.Pointer類型指針的操作也是不安全的。比如下面的例子需要將變量x的地址加上b字段地址偏移量轉化爲`*int16`類型指針,然後通過該指針更新x.b: +gopl.io/ch13/unsafeptr ```Go -//gopl.io/ch13/unsafeptr - var x struct { a bool b int16 @@ -37,10 +36,10 @@ fmt.Println(x.b) // "42" 上面的寫法盡管很繁瑣,但在這里併不是一件壞事,因爲這些功能應該很謹慎地使用。不要試圖引入一個uintptr類型的臨時變量,因爲它可能會破壞代碼的安全性(譯註:這是眞正可以體會unsafe包爲何不安全的例子)。下面段代碼是錯誤的: ```Go -// NOTE: subtly incorrect! -tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b) -pb := (*int16)(unsafe.Pointer(tmp)) -*pb = 42 +// NOTE: subtly incorrect! +tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b) +pb := (*int16)(unsafe.Pointer(tmp)) +*pb = 42 ``` 産生錯誤的原因很微妙。有時候垃圾迴收器會移動一些變量以降低內存碎片等問題。這類垃圾迴收器被稱爲移動GC。當一個變量被移動,所有的保存改變量舊地址的指針必須同時被更新爲變量移動後的新地址。從垃圾收集器的視角來看,一個unsafe.Pointer是一個指向變量的指針,因此當變量被移動是對應的指針也必須被更新;但是uintptr類型的臨時變量隻是一個普通的數字,所以其值不應該被改變。上面錯誤的代碼因爲引入一個非指針的臨時變量tmp,導致垃圾收集器無法正確識别這個是一個指向變量x的指針。當第二個語句執行時,變量x可能已經被轉移,這時候臨時變量tmp也就不再是現在的`&x.b`地址。第三個向之前無效地址空間的賦值語句將徹底摧譭整個程序! @@ -66,5 +65,3 @@ func (Value) Pointer() uintptr func (Value) UnsafeAddr() uintptr func (Value) InterfaceData() [2]uintptr // (index 1) ``` - - diff --git a/ch13/ch13-03.md b/ch13/ch13-03.md index 0d32a6a..7d6a87a 100644 --- a/ch13/ch13-03.md +++ b/ch13/ch13-03.md @@ -22,8 +22,8 @@ fmt.Println(reflect.DeepEqual(c, d)) // "false" 我們希望在這里實現一個自己的Equal函數,用於比較類型的值。和DeepEqual函數類似的地方是它也是基於slice和map的每個元素進行遞歸比較,不同之處是它將nil值的slice(map類似)和非nil值但是空的slice視作相等的值。基礎部分的比較可以基於reflect包完成,和12.3章的Display函數的實現方法類似。同樣,我們也定義了一個內部函數equal,用於內部的遞歸比較。讀者目前不用關心seen參數的具體含義。對於每一對需要比較的x和y,equal函數首先檢測它們是否都有效(或都無效),然後檢測它們是否是相同的類型。剩下的部分是一個鉅大的switch分支,用於相同基礎類型的元素比較。因爲頁面空間的限製,我們省略了一些相似的分支。 +gopl.io/ch13/equal ```Go -gopl.io/ch13/equal func equal(x, y reflect.Value, seen map[comparison]bool) bool { if !x.IsValid() || !y.IsValid() { return x.IsValid() == y.IsValid() @@ -125,5 +125,3 @@ fmt.Println(Equal(a, c)) // "false" **練習 13.1:** 定義一個深比較函數,對於十億以內的數字比較,忽略類型差異。 **練習 13.2:** 編寫一個函數,報告其參數是否循環數據結構。 - - diff --git a/ch13/ch13-04.md b/ch13/ch13-04.md index f7ab015..c071e53 100644 --- a/ch13/ch13-04.md +++ b/ch13/ch13-04.md @@ -41,9 +41,8 @@ bzip2壓縮算法,是基於優雅的Burrows-Wheeler變換算法,運行速度 我們可以在Go代碼中直接調用BZ2_bzCompressInit和BZ2_bzCompressEnd,但是對於BZ2_bzCompress,我們將定義一個C語言的包裝函數,用它完成眞正的工作。下面是C代碼,對應一個獨立的文件。 +gopl.io/ch13/bzip ```C -gopl.io/ch13/bzip - /* This file is gopl.io/ch13/bzip/bzip2.c, */ /* a simple wrapper for libbzip2 suitable for cgo. */ #include @@ -168,9 +167,8 @@ func (w *writer) Close() error { 下面的bzipper程序,使用我們自己包實現的bzip2壓縮命令。它的行爲和許多Unix繫統的bzip2命令類似。 +gopl.io/ch13/bzipper ```Go -gopl.io/ch13/bzipper - // Bzipper reads input, bzip2-compresses it, and writes it out. package main @@ -211,4 +209,3 @@ $ ./bzipper < /usr/share/dict/words | bunzip2 | sha256sum **練習 13.3:** 使用sync.Mutex以保證bzip2.writer在多個goroutines中被併發調用是安全的。 **練習 13.4:** 因爲C庫依賴的限製。 使用os/exec包啟動/bin/bzip2命令作爲一個子進程,提供一個純Go的bzip.NewWriter的替代實現(譯註:雖然是純Go實現,但是運行時將依賴/bin/bzip2命令,其他操作繫統可能無法運行)。 -