make loop

This commit is contained in:
chai2010
2015-12-26 20:05:30 +08:00
parent 82ec0c025d
commit e15e88dad7
74 changed files with 207 additions and 207 deletions

View File

@@ -36,7 +36,7 @@ struct{ float64; int16; bool } // 2 words 3words
struct{ bool; int16; float64 } // 2 words 3words
```
關於內存地址對齊算法的細節超了本書的范圍也不是每一個結構體都需要擔心這個問題不過有效的包裝可以使數據結構更加緊湊譯註未來的Go語言編譯器應該會默認優化結構體的順序當然用於應該也能夠指定具體的內存布局相同討論請參考 [Issue10014](https://github.com/golang/go/issues/10014) ),內存使用率和性能都可能會受益。
關於內存地址對齊算法的細節超了本書的范圍也不是每一個結構體都需要擔心這個問題不過有效的包裝可以使數據結構更加緊湊譯註未來的Go語言編譯器應該會默認優化結構體的順序當然用於應該也能夠指定具體的內存布局相同討論請參考 [Issue10014](https://github.com/golang/go/issues/10014) ),內存使用率和性能都可能會受益。
`unsafe.Alignof` 函數返迴對應參數的類型需要對齊的倍數. 和 Sizeof 類似, Alignof 也是返迴一個常量表達式, 對應一個常量. 通常情況下布爾和數字類型需要對齊到它們本身的大小(最多8個字節), 其它的類型對齊到機器字大小.

View File

@@ -63,7 +63,7 @@ func equal(x, y reflect.Value, seen map[comparison]bool) bool {
}
```
和前面的建議一樣我們併不公開reflect包相關的接口所以導的函數需要在內部自己將變量轉爲reflect.Value類型。
和前面的建議一樣我們併不公開reflect包相關的接口所以導的函數需要在內部自己將變量轉爲reflect.Value類型。
```Go
// Equal reports whether x and y are deeply equal.
@@ -78,7 +78,7 @@ type comparison struct {
}
```
爲了確保算法對於有環的數據結構也能正常退我們必鬚記録每次已經比較的變量從而避免進入第二次的比較。Equal函數分配了一組用於比較的結構體包含每對比較對象的地址unsafe.Pointer形式保存和類型。我們要記録類型的原因是有些不同的變量可能對應相同的地址。例如如果x和y都是數組類型那麽x和x[0]將對應相同的地址y和y[0]也是對應相同的地址這可以用於區分x與y之間的比較或x[0]與y[0]之間的比較是否進行過了。
爲了確保算法對於有環的數據結構也能正常退我們必鬚記録每次已經比較的變量從而避免進入第二次的比較。Equal函數分配了一組用於比較的結構體包含每對比較對象的地址unsafe.Pointer形式保存和類型。我們要記録類型的原因是有些不同的變量可能對應相同的地址。例如如果x和y都是數組類型那麽x和x[0]將對應相同的地址y和y[0]也是對應相同的地址這可以用於區分x與y之間的比較或x[0]與y[0]之間的比較是否進行過了。
```Go
// cycle check

View File

@@ -4,7 +4,7 @@ Go程序可能會遇到要訪問C語言的某些硬件驅動函數的場景
在本節中我們將構建一個簡易的數據壓縮程序使用了一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具。這類工具一般被稱爲 *foreign-function interfaces* 簡稱ffi, 併且在類似工具中cgo也不是唯一的。SWIG http://swig.org 是另一個類似的且被廣泛使用的工具SWIG提供了很多複雜特性以支援C++的特性但SWIG併不是我們要討論的主題。
在標準庫的`compress/...`子包有很多流行的壓縮算法的編碼和解碼實現包括流行的LZW壓縮算法Unix的compress命令用的算法和DEFLATE壓縮算法GNU gzip命令用的算法。這些包的API的細節雖然有些差異但是它們都提供了針對 io.Writer類型輸的壓縮接口和提供了針對io.Reader類型輸入的解壓縮接口。例如
在標準庫的`compress/...`子包有很多流行的壓縮算法的編碼和解碼實現包括流行的LZW壓縮算法Unix的compress命令用的算法和DEFLATE壓縮算法GNU gzip命令用的算法。這些包的API的細節雖然有些差異但是它們都提供了針對 io.Writer類型輸的壓縮接口和提供了針對io.Reader類型輸入的解壓縮接口。例如
```Go
package gzip // compress/gzip
@@ -16,7 +16,7 @@ bzip2壓縮算法是基於優雅的Burrows-Wheeler變換算法運行速度
如果是比較小的C語言庫我們完全可以用純Go語言重新實現一遍。如果我們對性能也沒有特殊要求的話我們還可以用os/exec包的方法將C編寫的應用程序作爲一個子進程運行。隻有當你需要使用複雜而且性能更高的底層C接口時就是使用cgo的場景了譯註用os/exec包調用子進程的方法會導致程序運行時依賴那個應用程序。下面我們將通過一個例子講述cgo的具體用法。
譯註:本章采用的代碼都是最新的。因爲之前已經版的書中包含的代碼隻能在Go1.5之前使用。從Go1.6開始Go語言已經明確規定了哪些Go語言指針可以之間傳入C語言函數。新代碼重點是增加了bz2alloc和bz2free的兩個函數用於bz_stream對象空間的申請和釋放操作。下面是新代碼中增加的註釋説明這個問題
譯註:本章采用的代碼都是最新的。因爲之前已經版的書中包含的代碼隻能在Go1.5之前使用。從Go1.6開始Go語言已經明確規定了哪些Go語言指針可以之間傳入C語言函數。新代碼重點是增加了bz2alloc和bz2free的兩個函數用於bz_stream對象空間的申請和釋放操作。下面是新代碼中增加的註釋説明這個問題
```Go
// The version of this program that appeared in the first and second
@@ -37,7 +37,7 @@ bzip2壓縮算法是基於優雅的Burrows-Wheeler變換算法運行速度
// pointers to Go variables.
```
要使用libbzip2我們需要先構建一個bz_stream結構體用於保持輸入和輸緩存。然後有三個函數BZ2_bzCompressInit用於初始化緩存BZ2_bzCompress用於將輸入緩存的數據壓縮到輸緩存BZ2_bzCompressEnd用於釋放不需要的緩存。目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的。)
要使用libbzip2我們需要先構建一個bz_stream結構體用於保持輸入和輸緩存。然後有三個函數BZ2_bzCompressInit用於初始化緩存BZ2_bzCompress用於將輸入緩存的數據壓縮到輸緩存BZ2_bzCompressEnd用於釋放不需要的緩存。目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的。)
我們可以在Go代碼中直接調用BZ2_bzCompressInit和BZ2_bzCompressEnd但是對於BZ2_bzCompress我們將定義一個C語言的包裝函數用它完成眞正的工作。下面是C代碼對應一個獨立的文件。
@@ -106,7 +106,7 @@ func NewWriter(out io.Writer) io.WriteCloser {
在cgo註釋中還可以包含#cgo指令用於給C語言工具鏈指定特殊的參數。例如CFLAGS和LDFLAGS分别對應傳給C語言編譯器的編譯參數和鏈接器參數使它們可以特定目録找到bzlib.h頭文件和libbz2.a庫文件。這個例子假設你已經在/usr目録成功安裝了bzip2庫。如果bzip2庫是安裝在不同的位置你需要更新這些參數。
NewWriter函數通過調用C語言的BZ2_bzCompressInit函數來初始化stream中的緩存。在writer結構中還包括了另一個buffer用於輸緩存。
NewWriter函數通過調用C語言的BZ2_bzCompressInit函數來初始化stream中的緩存。在writer結構中還包括了另一個buffer用於輸緩存。
下面是Write方法的實現返迴成功壓縮數據的大小主體是一個循環中調用C語言的bz2compress函數實現的。從代碼可以看到Go程序可以訪問C語言的bz_stream、char和uint類型還可以訪問bz2compress等函數甚至可以訪問C語言中像BZ_RUN那樣的宏定義全部都是以C.x語法訪問。其中C.uint類型和Go語言的uint類型併不相同卽使它們具有相同的大小也是不同的類型。
@@ -132,9 +132,9 @@ func (w *writer) Write(data []byte) (int, error) {
}
```
在循環的每次迭代中向bz2compress傳入數據的地址和剩餘部分的長度還有輸緩存w.outbuf的地址和容量。這兩個長度信息通過它們的地址傳入而不是值傳入因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值。每個塊壓縮後的數據被寫入到底層的io.Writer。
在循環的每次迭代中向bz2compress傳入數據的地址和剩餘部分的長度還有輸緩存w.outbuf的地址和容量。這兩個長度信息通過它們的地址傳入而不是值傳入因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值。每個塊壓縮後的數據被寫入到底層的io.Writer。
Close方法和Write方法有着類似的結構通過一個循環將剩餘的壓縮數據刷新到輸緩存。
Close方法和Write方法有着類似的結構通過一個循環將剩餘的壓縮數據刷新到輸緩存。
```Go
// Close flushes the compressed data and closes the stream.
@@ -162,7 +162,7 @@ func (w *writer) Close() error {
}
```
壓縮完成後Close方法用了defer函數確保函數退前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C語言運行時資源。此刻w.stream指針將不再有效我們將它設置爲nil以保證安全然後在每個方法中增加了nil檢測以防止用戶在關閉後依然錯誤使用相關方法。
壓縮完成後Close方法用了defer函數確保函數退前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C語言運行時資源。此刻w.stream指針將不再有效我們將它設置爲nil以保證安全然後在每個方法中增加了nil檢測以防止用戶在關閉後依然錯誤使用相關方法。
上面的實現中不僅僅寫是非併發安全的甚至併發調用Close和Write方法也可能導致程序的的崩潰。脩複這個問題是練習13.3的內容。

View File

@@ -1,6 +1,6 @@
# 第13章 底層編程
Go語言的設計包含了諸多安全策略限製了可能導致程序運行現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型都有嚴格的類型轉換規則。
Go語言的設計包含了諸多安全策略限製了可能導致程序運行現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型都有嚴格的類型轉換規則。
對於無法靜態檢測到的錯誤,例如數組訪問越界或使用空指針,運行時動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息。自動內存管理(垃圾內存自動迴收)可以消除大部分野指針和內存洩漏相關的問題。