mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-02-04 17:02:46 +08:00
rebuild
This commit is contained in:
2107
CONTRIBUTORS.html
Normal file
2107
CONTRIBUTORS.html
Normal file
File diff suppressed because it is too large
Load Diff
16
Makefile
16
Makefile
@@ -8,6 +8,8 @@
|
||||
# https://github.com/GitbookIO/gitbook
|
||||
# https://github.com/wastemobile/gitbook
|
||||
|
||||
# http://www.imagemagick.org/
|
||||
|
||||
default:
|
||||
gitbook build
|
||||
|
||||
@@ -21,3 +23,17 @@ loop:
|
||||
go run zh2tw.go . .md$$ tw2zh
|
||||
go run zh2tw.go . .md$$ zh2tw
|
||||
|
||||
review:
|
||||
go run zh2tw.go . .md$$ tw2zh
|
||||
gitbook build
|
||||
go run zh2tw.go . .md$$ zh2tw
|
||||
|
||||
cover:
|
||||
composite cover_patch.png cover_bgd.png cover.jpg
|
||||
convert -resize 1800x2360! cover.jpg cover.jpg
|
||||
convert -resize 200x262! cover.jpg cover_small.jpg
|
||||
convert -quality 75% cover.jpg cover.jpg
|
||||
convert -quality 75% cover_small.jpg cover_small.jpg
|
||||
convert -strip cover.jpg cover.jpg
|
||||
convert -strip cover_small.jpg cover_small.jpg
|
||||
|
||||
|
||||
32
README.md
32
README.md
@@ -1,27 +1,27 @@
|
||||
# 關於 [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh)
|
||||
# Go語言聖經(中文版)
|
||||
|
||||
作爲 [《The Go Programming Language》](http://gopl.io/) (中文名[《Go編程語言》](http://golang-china.github.io/gopl-zh)) 英文原版紙質圖書的購買者, [《Go聖經讀書筆記》](http://golang-china.github.io/gopl-zh) 是我們的 **讀書筆記** 和 **習題解答**, 僅供學習交流用.
|
||||
Go語言聖經 [《The Go Programming Language》](http://gopl.io) 中文版本,僅供編程和英語學習交流之用,請在下載後24小時內刪除。
|
||||
|
||||
- 此 **讀書筆記** 在線預覽: http://golang-china.github.io/gopl-zh
|
||||
- 此 **讀書筆記** 的源文件: http://github.com/golang-china/gopl-zh
|
||||
- 此 **讀書筆記** 項目進度: http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
||||
- 此 **讀書筆記** 參與人員: http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||
- 原版官網: http://gopl.io
|
||||
- 項目主頁:http://github.com/golang-china/gopl-zh
|
||||
- 項目進度:http://github.com/golang-china/gopl-zh/blob/master/progress.md
|
||||
- 參與人員:http://github.com/golang-china/gopl-zh/blob/master/CONTRIBUTORS.md
|
||||
- 在線預覽:http://golang-china.github.io/gopl-zh
|
||||
- 原版官網:http://gopl.io
|
||||
|
||||
[](https://github.com/golang-china/gopl-zh)
|
||||
|
||||
|
||||
### 從源文件構建:
|
||||
### 從源文件構建
|
||||
|
||||
先安裝 Go語言環境, git 工具 和 GitBook 命令行工具(`npm install gitbook-cli -g` 命令).
|
||||
先安裝Go語言環境,Git工具和GitBook命令行工具(`npm install gitbook-cli -g`命令)。
|
||||
|
||||
1. 運行 `go get github.com/golang-china/gopl-zh`, 穫取 源文件
|
||||
2. 運行 `go generate github.com/golang-china/gopl-zh`, 生成 `_book` 目録
|
||||
3. 打開 `_book/index.html` 文件
|
||||
1. 運行`go get github.com/golang-china/gopl-zh`,穫取源文件。
|
||||
2. 運行`go generate github.com/golang-china/gopl-zh`,生成`_book`目録。
|
||||
3. 打開`_book/index.html`文件。
|
||||
|
||||
### 簡體中文讀者
|
||||
|
||||
如果是使用簡體中文的用戶, 可在執行上述命令前運行 `make tw2zh` 命令, 將繁體中文轉換爲簡體中文.
|
||||
如果是使用簡體中文的用戶,可在執行上述命令前運行`make tw2zh`命令,將繁體中文轉換爲簡體中文。
|
||||
|
||||
### Markdown 格式預覽
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
|
||||
# 版權聲明
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>.
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License</a>。
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="./images/by-nc-sa-4.0-88x31.png"></img></a>
|
||||
|
||||
嚴禁任何商業行爲使用或引用該 **讀書筆記** 的全部或部分內容!
|
||||
嚴禁任何商業行爲使用或引用該文檔的全部或部分內容!
|
||||
|
||||
歡迎大家提供建議!
|
||||
歡迎大家提供建議!
|
||||
|
||||
|
||||
@@ -149,5 +149,5 @@
|
||||
* [13.3. 示例: 深度相等判斷](ch13/ch13-03.md)
|
||||
* [13.4. 通過cgo調用C代碼](ch13/ch13-04.md)
|
||||
* [13.5. 幾點忠告](ch13/ch13-05.md)
|
||||
* [勘誤](errata.md)
|
||||
* [附録](CONTRIBUTORS.md)
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.1" data-chapter-title="Go語言起源" data-filepath="ch0/ch0-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.1" data-chapter-title="Go語言起源" data-filepath="ch0/ch0-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,16 +2024,15 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="go語言起源">Go語言起源</h2>
|
||||
<p>就像生物物種, 一個成功的編程語言的後代一般都會繼承它們祖先的優點; 當然有時多種語言混合也會産生令人驚訝的特性; 還有一些激進的新特性可能併沒有先例. 我們可以通過觀察語言的和環境是如何相互促進和影響的演化過程而學到很多.</p>
|
||||
<p>下圖展示了最早期的編程語言對Go語言設計産生的重要影響.</p>
|
||||
<p>編程語言的演化就像生物物種的演化類似,一個成功的編程語言的後代一般都會繼承它們祖先的優點;當然有時多種語言雜合也可能會産生令人驚訝的特性;還有一些激進的新特性可能併沒有先例。我們可以通過觀察編程語言和軟硬件環境是如何相互促進、相互影響的演化過程而學到很多。</p>
|
||||
<p>下圖展示了有哪些早期的編程語言對Go語言的設計産生了重要影響。</p>
|
||||
<p><img src="../images/ch0-01.png" alt=""></p>
|
||||
<p>Go有時候被描述爲"C類似語言", 或者是"21世紀的C語言". Go從C語言繼承了相似的表達式語法, 控製流結構, 基礎數據類型, 調用參數傳值, 指針等很多思想, 還有C語言一直看中的編譯後機器碼的運行效率以及和現有操作繫統的無縫的適配.</p>
|
||||
<p>但是在Go語言家的族樹中還有其他的祖先. 其中一個有影響的分支來自Niklaus Wirth設計的Pascal語言. Modula-2 激發了包的概念. Oberon 摒棄了模塊接口文件和模塊實現文件之間的區别. Oberon-2 影響了的包的導入和聲明的語法, 還有 面向對象 Oberon 所提供的方法的聲明語法等.</p>
|
||||
<p>Go的另一支祖先, 也是Go區别其他語言的重要特性, 靈感來自貝爾實驗室的Tony Hoare的1978年發表的鮮爲外界所知的關於併發研究的基礎文獻communicating sequential processes (CSP). 在CSP中, 程序是一組中間沒有共享狀態的平行的處理過程, 它們使用管道進行通信和同步. 不過Tony Hoare的CSP隻是一個用於描述併發性基本概念的描述語言, 併不是一個編寫可執行程序的編程語言.</p>
|
||||
<p>Rob Pike和其他人開始嚐試將CSP引入實際的編程語言中. 第一個語言叫Squeak(老鼠間交流的語言), 一個提供鼠標和鍵盤事件處理的語言, 它的管道是靜態創建的. 然後是Newsqueak, 提供了類似C語言語句和表達式的的語法和Pascal的類似推導語法. 它是一個帶垃圾迴收的純函數式語言, 再此針對管理鍵盤, 鼠標和窗口事件管理. 但是Newsqueak中管道是動態創建的, 屬於第一類值, 可以保存到變量中.</p>
|
||||
<p>在Plan9操作繫統中, 這些想法被吸收到一個叫Alef的編程語言中. Alef視圖將Newsqueak改造爲繫統編程語言, 但是因爲缺少垃圾迴收機製而導致併發處理很痛苦.</p>
|
||||
<p>Go的其他一些特性零散地來着其他的一些語言; 比如 iota 從 APL 借鑒, 詞法作用域與嵌套函數來自 Scheme (和其他很多語言). 我們也可以從Go中發現很多創新的設計. 比如Go的切片爲動態數組提供了有效的隨機存取性能, 以及可能會讓人聯想到鏈表的底層的共享機製.
|
||||
還有Go自己發明的defer語句.</p>
|
||||
<p>Go語言有時候被描述爲“C類似語言”,或者是“21世紀的C語言”。Go從C語言繼承了相似的表達式語法、控製流結構、基礎數據類型、調用參數傳值、指針等很多思想,還有C語言一直所看中的編譯後機器碼的運行效率以及和現有操作繫統的無縫適配。</p>
|
||||
<p>但是在Go語言的家族樹中還有其它的祖先。其中一個有影響力的分支來自Niklaus Wirth所設計的Pascal語言。然後Modula-2語言激發了包的概念。然後Oberon語言 摒棄了模塊接口文件和模塊實現文件之間的區别。第二代的Oberon-2語言直接影響了包的導入和聲明的語法,還有Oberon語言的面向對象特性所提供的方法的聲明語法等。</p>
|
||||
<p>Go語言的另一支祖先,帶來了Go語言區别其他語言的重要特性,靈感來自於貝爾實驗室的Tony Hoare於1978年發表的鮮爲外界所知的關於併發研究的基礎文獻 <em>順序通信進程</em> ( <em>communicating sequential processes</em> ,縮寫爲CSP)。在CSP中,程序是一組中間沒有共享狀態的平行運行的處理過程,它們之間使用管道進行通信和控製同步。不過Tony Hoare的CSP隻是一個用於描述併發性基本概念的描述語言,併不是一個可以編寫可執行程序的通用編程語言。</p>
|
||||
<p>接下來,Rob Pike和其他人開始不斷嚐試將CSP引入實際的編程語言中。他們第一次嚐試引入CSP特性的編程語言叫Squeak(老鼠間交流的語言),是一個提供鼠標和鍵盤事件處理的編程語言,它的管道是靜態創建的。然後是改進版的Newsqueak語言,提供了類似C語言語句和表達式的語法和類似Pascal語言的推導語法。Newsqueak是一個帶垃圾迴收的純函數式語言,它再次針對鍵盤、鼠標和窗口事件管理。但是在Newsqueak語言中管道是動態創建的,屬於第一類值, 可以保存到變量中。</p>
|
||||
<p>在Plan9操作繫統中,這些優秀的想法被吸收到了一個叫Alef的編程語言中。Alef試圖將Newsqueak語言改造爲繫統編程語言,但是因爲缺少垃圾迴收機製而導致併發編程很痛苦。(譯註:在Aelf之後還有一個叫Limbo的編程語言,Go語言從其中借鑒了很多特性。在docs目録包含了這些語言相關的文檔手冊。 具體請參考Pike的講稿:<a href="http://talks.golang.org/2012/concurrency.slide#9" target="_blank">http://talks.golang.org/2012/concurrency.slide#9</a> )</p>
|
||||
<p>Go語言的其他的一些特性零散地來自於其他一些編程語言;比如iota語法是從APL語言借鑒,詞法作用域與嵌套函數來自於Scheme語言(和其他很多語言)。當然,我們也可以從Go中發現很多創新的設計。比如Go語言的切片爲動態數組提供了有效的隨機存取的性能,這可能會讓人聯想到鏈表的底層的共享機製。還有Go語言新發明的defer語句。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.2" data-chapter-title="Go語言項目" data-filepath="ch0/ch0-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.2" data-chapter-title="Go語言項目" data-filepath="ch0/ch0-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,14 +2024,13 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="go語言項目">Go語言項目</h2>
|
||||
<p>所有的編程語言都反映了設計者對編程哲學的反思, 通常包括之前的語言所暴露的一些不足的地方.
|
||||
Go項目是在Google超級複雜的幾個軟件繫統遇到的一些問題的反思(但是這個問題絶不是谷歌特有的).</p>
|
||||
<p>正如Rob Pike所説, “複雜性是乘法級相關的”, 通過增加一個部分的複雜性來脩複問題通常將慢慢地增加其他部分的複雜性. 通過增加功能和選項和配置是脩複問題的最快的途徑, 但是這很容易忽略簡潔的內涵, 卽使從長遠來看, 簡潔依然是好的軟件關鍵因素.</p>
|
||||
<p>簡潔需要在工作開始的時候減少不必要的想法, 併且在軟件的生命週期內嚴格區别好的改變或壞的改變. 通過足夠的努力, 一個好的改變可以在不破壞完整概念的前提下保持自適應, 正如 Fred Brooks 所説的 "概念完整性"; 而一個壞的改變則不能, 它們僅僅是通過膚淺的簡單的妥協來破壞設計的一致性. 隻有通過簡潔的設計, 纔能讓一個繫統保持穩定, 安全, 和持續的生長.</p>
|
||||
<p>Go項目包括語言本身, 附帶的工具和標準庫, 最後但併非不重要的, 簡潔編程哲學的宣言. 就事後的目光來看, Go的這些地方都做的不錯: 擁有自動垃圾迴收, 一個包繫統, 函數作爲一等公民, 詞法作用域, 繫統調用接口, 隻讀的UTF8字符串. 但是Go隻有相對較少的特性, 也不太可能對添加更多. 例如, 它沒有隱式的數值轉換, 沒有構造函數和析構函數, 沒有運算符重載, 沒有默認參數, 沒有繼承, 沒有泛型, 沒有異常, 沒有宏, 沒有函數脩飾, 沒有線程局部存儲. 但是語言是成熟和穩定的, 而且保證向後兼容: 以前的Go程序可以用新版本的編譯器和標準庫下構建.</p>
|
||||
<p>Go有足夠的類型繫統以避免動態語言中那些粗心的類型錯誤, 但是Go的類型繫統相比傳統的強類型語言又要簡潔很多. 有時候這會導致一個"無類型"的抽象類型, 但是Go程序員併不需要像 C++ 或 Haskell 程序員那樣糾結具體類型的安全屬性. 但實踐中Go的簡潔的類型繫統給了程序員更多的安全性和更好的運行時性能.</p>
|
||||
<p>Go 鼓勵當代計算機繫統設計的認識, 特别是局部的重要性. 它的內置數據類型和大多數的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數, 因此較少的內存分配和內存初始化被隱藏在了代碼中. Go的聚合類型(結構體和數組)直接操作它們的元素, 需要更少的存儲空間, 更少的內存分配, 而且指針操作比其他間接語言也更直接. 由於現代計算機是一個併行的機器, Go提供了基於CSP的併發特性. Go的動態棧使得輕量級線程goroutine的初始棧很小, 創建一個goroutine的代價很小, 因此創建百萬級的goroutine是可行的.</p>
|
||||
<p>Go的標準庫(通常被稱爲自帶的電池), 提供了清晰的構建模塊和接口, 包含 I/O, 文本處理, 圖像, 密碼學, 網絡, 和分布式應用程序, 併支持許多標準的文件格式和協議. 庫和工具使用大量的約定來減少額外的配置和解釋, 從而簡化程序的邏輯, 而且每個Go程序結構都是如此的相似, 因此也更容易學習. 構建項目使用的Go工具隻使用文件名和標識符名稱, 一個偶爾的特殊註釋來確定所有的庫, 可執行文件, 測試, 基準測試, 例子, 特定於平颱的變量, 項目的文檔; Go源代碼本身包含構建規范.</p>
|
||||
<p>所有的編程語言都反映了語言設計者對編程哲學的反思,通常包括之前的語言所暴露的一些不足地方的改進。Go項目是在Google公司維護超級複雜的幾個軟件繫統遇到的一些問題的反思(但是這類問題絶不是Google公司所特有的)。</p>
|
||||
<p>正如Rob Pike所説,“軟件的複雜性是乘法級相關的”,通過增加一個部分的複雜性來脩複問題通常將慢慢地增加其他部分的複雜性。通過增加功能和選項和配置是脩複問題的最快的途徑,但是這很容易讓人忘記簡潔的內涵,卽使從長遠來看,簡潔依然是好軟件的關鍵因素。</p>
|
||||
<p>簡潔的設計需要在工作開始的時候舍棄不必要的想法,併且在軟件的生命週期內嚴格區别好的改變或壞的改變。通過足夠的努力,一個好的改變可以在不破壞原有完整概念的前提下保持自適應,正如Fred Brooks所説的“概念完整性”;而一個壞的改變則不能達到這個效果,它們僅僅是通過膚淺的和簡單的妥協來破壞原有設計的一致性。隻有通過簡潔的設計,纔能讓一個繫統保持穩定、安全和持續的進化。</p>
|
||||
<p>Go項目包括編程語言本身,附帶了相關的工具和標準庫,最後但併非代表不重要的,關於簡潔編程哲學的宣言。就事後諸葛的角度來看,Go語言的這些地方都做的還不錯:擁有自動垃圾迴收、一個包繫統、函數作爲一等公民、詞法作用域、繫統調用接口、隻讀的UTF8字符串等。但是Go語言本身隻有很少的特性,也不太可能添加太多的特性。例如,它沒有隱式的數值轉換,沒有構造函數和析構函數,沒有運算符重載,沒有默認參數,也沒有繼承,沒有泛型,沒有異常,沒有宏,沒有函數脩飾,更沒有線程局部存儲。但是語言本身是成熟和穩定的,而且承諾保證向後兼容:用之前的Go語言編寫程序可以用新版本的Go語言編譯器和標準庫直接構建而不需要脩改代碼。</p>
|
||||
<p>Go語言有足夠的類型繫統以避免動態語言中那些粗心的類型錯誤,但是Go語言的類型繫統相比傳統的強類型語言又要簡潔很多。雖然有時候這會導致一個“無類型”的抽象類型概念,但是Go語言程序員併不需要像C++或Haskell程序員那樣糾結於具體類型的安全屬性。在實踐中Go語言簡潔的類型繫統給了程序員帶來了更多的安全性和更好的運行時性能。</p>
|
||||
<p>Go語言鼓勵當代計算機繫統設計的原則,特别是局部的重要性。它的內置數據類型和大多數的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數,因爲很少的內存分配和內存初始化代碼被隱藏在庫代碼中了。Go語言的聚合類型(結構體和數組)可以直接操作它們的元素,隻需要更少的存儲空間、更少的內存分配,而且指針操作比其他間接操作的語言也更有效率。由於現代計算機是一個併行的機器,Go語言提供了基於CSP的併發特性支持。Go語言的動態棧使得輕量級線程goroutine的初始棧可以很小,因此創建一個goroutine的代價很小,創建百萬級的goroutine完全是可行的。</p>
|
||||
<p>Go語言的標準庫(通常被稱爲語言自帶的電池),提供了清晰的構建模塊和公共接口,包含I/O操作、文本處理、圖像、密碼學、網絡和分布式應用程序等,併支持許多標準化的文件格式和編解碼協議。庫和工具使用了大量的約定來減少額外的配置和解釋,從而最終簡化程序的邏輯,而且每個Go程序結構都是如此的相似,因此Go程序也很容易學習。使用Go語言自帶工具構建Go語言項目隻需要使用文件名和標識符名稱, 一個偶爾的特殊註釋來確定所有的庫、可執行文件、測試、基準測試、例子、以及特定於平颱的變量、項目的文檔等;Go語言源代碼本身就包含了構建規范。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.3" data-chapter-title="本書的組織" data-filepath="ch0/ch0-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.3" data-chapter-title="本書的組織" data-filepath="ch0/ch0-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,35 +2024,26 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="本書的組織">本書的組織</h2>
|
||||
<p>我們假設你有一個或多個其他編程語言的使用經歷, 不過是類似 C、c++,和Java 的編譯型語言,
|
||||
還是類似 Python, Ruby, JavaScript 的腳本語言, 因此我們不會相對完全的編程語言初學者那樣解釋所有的細節.
|
||||
因爲Go的語言的 變量,常量,表達式,控製流和函數等語法也是類似的.</p>
|
||||
<p>第一章包含了Go敎程的基本結構, 通過十幾個程序介紹了用Go如何實現 類似讀寫文件, 文本格式化, 創建圖像,
|
||||
網絡客戶端和服務器通訊 等日常工作.</p>
|
||||
<p>第二章描述了一個Go程序的基本元素結構, 變量, 定義新的類型, 包和文件, 和作用域. 第三章討論了數字, 布爾值, 字符串和常量, 併演示了如何顯示和處理Unicode. 第四章描述了複合類型, 從簡單的數組, 字典, 切片, 到動態列表. 第五章涵蓋了函數, 併討論了錯誤處理, panic 和 recover, 和 defer 語句.</p>
|
||||
<p>第三章討論了數字、布爾值、字符串和常數,併解釋顯示處理Unicode。
|
||||
第四章描述了複合類型,類型建立使用數組,從簡單的地圖,結構,和切割的方法去動態列表。第五章涵蓋了函數和討論錯誤處理,恐慌和恢複,而推遲的陳述。</p>
|
||||
<p>第一章到第五章是基礎部分, 任何主流命令式語言的一部分都是類似的. 雖然有時候Go的語法和風格會有自己的特色, 但是大多數程序員將很快適應.
|
||||
剩下的章節是Go中特有的部分: 方法, 接口, 併發, 包, 測試和反射等.</p>
|
||||
<p>Go的面向對象是不同尋常的. 它沒有類層次結構, 甚至沒有類; 僅僅是通過組合(而不是繼承)簡單的對象來構建複雜的對象.
|
||||
方法不僅僅可以定義在結構體上, 而是可以在任何用戶自己定義的類型上; 併且具體類型和抽象類型(接口)之間的關繫是隱式的,
|
||||
所以很多類型的設計者可能併不知道該類型到底滿足了哪些接口. 方法在第六章討論, 接口在第七章將討論.</p>
|
||||
<p>第八章討論了基於順序通信進程(CSP)的概念的併發編程, 使用 goroutines 和 channels. 第九章討論了更爲傳統的基於共享變量的併發性.</p>
|
||||
<p>第十章描述了包機製, 包的組織結構. 本章還展示了如何利用有效的利用Go自帶的工具,
|
||||
通過一個命令提供了編譯, 測試, 基準測試, 代碼格式化, 文檔, 和許多其他任務.</p>
|
||||
<p>第十一章討論單元測試, Go的工具和標準庫中集成了輕量級的測試功能, 從而避免了采用複雜強大的測試框架. 測試庫提供一些基本的構建, 如果有必要可以構建更複雜的測試抽象.</p>
|
||||
<p>第十二章討論了反射, 一個程序在運行期間來檢視自己的能力. 反射是一個強大的工具, 不過要謹慎地使用; 本章通過用反射實現一些重要的Go庫來展示了反射的用法. 第十三章解釋了低級編程的細節, 通過使用 unsafe 包來繞過Go的類型繫統, 有時這是必要的.</p>
|
||||
<p>每一章會有一些練習, 你可以根據你對Go語言的理解, 然後脩改書中的例子來探索Go的其他用法.</p>
|
||||
<p>書中所有的代碼都可以從 gopl.io 上的 Git 倉庫下載. go get可以根據每個例子的其導入路徑方便地穫取/構建/併安裝. 你需要選擇一個目録作爲工作空間, 然後將GOPATH環境指向這個工作目録.</p>
|
||||
<p>Go工具將在必要時創建的相應的目録. 例如:</p>
|
||||
<p>我們假設你已經有一個或多個其他編程語言的使用經歷,不管是類似C、c++或Java的編譯型語言,還是類似Python、Ruby、JavaScript的腳本語言,因此我們不會像對完全的編程語言初學者那樣解釋所有的細節。因爲Go語言的 變量、常量、表達式、控製流和函數等基本語法也是類似的。</p>
|
||||
<p>第一章包含了本敎程的基本結構,通過十幾個程序介紹了用Go語言如何實現 類似讀寫文件、文本格式化、創建圖像、網絡客戶端和服務器通訊等日常工作。</p>
|
||||
<p>第二章描述了一個Go語言程序的基本元素結構、變量、定義新的類型、包和文件、以及作用域的概念。第三章討論了數字、布爾值、字符串和常量,併演示了如何顯示和處理Unicode字符。第四章描述了複合類型,從簡單的數組、字典、切片到動態列表。第五章涵蓋了函數,併討論了錯誤處理、panic和recover,還有defer語句。</p>
|
||||
<p>第一章到第五章是基礎部分,對於任何主流命令式編程語言這個部分都是類似的。雖然有時候Go語言的語法和風格會有自己的特色,但是大多數程序員將能很快地適應。剩下的章節是Go語言中特有地方:方法、接口、併發、包、測試和反射等語言特性。</p>
|
||||
<p>Go語言的面向對象是不同尋常的。它沒有類層次結構,甚至可以説沒有類;僅僅是通過組合(而不是繼承)簡單的對象來構建複雜的對象。方法不僅僅可以定義在結構體上, 而且可以定義在任何用戶自己定義的類型上;併且具體類型和抽象類型(接口)之間的關繫是隱式的,所以很多類型的設計者可能併不知道該類型到底滿足了哪些接口。方法將在第六章討論,接口將在第七章將討論。</p>
|
||||
<p>第八章討論了基於順序通信進程(CSP)概念的併發編程,通過使用goroutines和channels處理併發編程。第九章則討論了更爲傳統的基於共享變量的併發編程。</p>
|
||||
<p>第十章描述了包機製和包的組織結構。這一章還展示了如何有效的利用Go自帶的工具,通過一個命令提供了編譯、測試、基準測試、代碼格式化、文檔和許多其他任務。</p>
|
||||
<p>第十一章討論了單元測試,Go語言的工具和標準庫中集成的輕量級的測試功能,從而避免了采用強大但複雜的測試框架。測試庫提供一些基本的構件,如果有必要可以用來構建更複雜的測試構件。</p>
|
||||
<p>第十二章討論了反射,一個程序在運行期間來審視自己的能力。反射是一個強大的編程工具,不過要謹慎地使用;這一章通過用利用反射機製實現一些重要的Go語言庫函數來展示了反射的強大用法。第十三章解釋了底層編程的細節,通過使用unsafe包來繞過Go語言安全的類型繫統,當然有時這是必要的。</p>
|
||||
<p>有些章節的後面可能會有一些練習,你可以根據你對Go語言的理解,然後脩改書中的例子來探索Go語言的其他用法。</p>
|
||||
<p>書中所有的代碼都可以從 <a href="http://gopl.io" target="_blank">http://gopl.io</a> 上的Git倉庫下載。go get命令可以根據每個例子的其導入路徑智能地穫取、構建併安裝。你隻需要選擇一個目録作爲工作空間,然後將GOPATH環境指向這個工作目録。</p>
|
||||
<p>Go語言工具將在必要時創建的相應的目録。例如:</p>
|
||||
<pre><code>$ export GOPATH=$HOME/gobook # 選擇工作目録
|
||||
$ go get gopl.io/ch1/helloworld # 穫取/編譯/安裝
|
||||
$ $GOPATH/bin/helloworld # 運行
|
||||
Hello, 世界 # 這是中文, 不是日文
|
||||
$ $GOPATH/bin/helloworld # 運行程序
|
||||
Hello, 世界 # 這是中文
|
||||
</code></pre><p>要運行這些例子, 你需要安裝Go1.5以上的版本.</p>
|
||||
<p>$ go version
|
||||
go version go1.5 linux/amd64</p>
|
||||
<p>如果你用的是其他的操作繫統, 請參考 <a href="https://golang.org/doc/install" target="_blank">https://golang.org/doc/install</a> 提供的説明安裝.</p>
|
||||
<pre><code>$ go version
|
||||
go version go1.5 linux/amd64
|
||||
</code></pre><p>如果你用的是其他的操作繫統, 請參考 <a href="https://golang.org/doc/install" target="_blank">https://golang.org/doc/install</a> 提供的説明安裝。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.4" data-chapter-title="更多的信息" data-filepath="ch0/ch0-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.4" data-chapter-title="更多的信息" data-filepath="ch0/ch0-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,12 +2024,12 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="更多的信息">更多的信息</h2>
|
||||
<p>最佳的信息來自Go的官方網站, <a href="https://golang.org" target="_blank">https://golang.org</a>, 它提供了訪問完善的參考文檔, 包括編程語言規范和標準庫等諸多信息. 同時也包含了如果更好了編寫Go程序的基本敎程, 還有各種各樣的在線文本資源和視頻資源, 它們是本身終有價值的補充. Go的博客 blog.golang.org 發布一些Go語言最好的實踐文章, 包括當前語言的狀態, 未來的計劃, 會議報告和Go相關的各種主題.</p>
|
||||
<p>在線訪問的一個有價值的地方是可以從web頁面運行Go的程序(而紙質書則沒有這麽便利了). 這個功能 由來自 play.golang.org 的 Go Playground 提供, 併且可以方便地嵌入到其他頁面, 例如 golang.org 的主頁, 或 godoc 提供的文檔中.</p>
|
||||
<p>Playground 可以簡單的通過執行一個小程序來測試對語法, 語義, 或對程序庫的理解, 類似其他很多語言提供的 REPL 卽時運行的方式. 同時它可以生成對應的url, 非常適合共享Go代碼片段, bug報告或提齣建議.</p>
|
||||
<p>基於 Playground 構建的 Go Tour (tour.golang.org), 是一個繫列的Go入門敎程, 包含了諸多基本概念和結構相關的可在線運行的互動小程序.</p>
|
||||
<p>Playground 和 Tour 也有一些不足, 它們隻能導入標準庫, 而且因爲安全的原因對一些網絡庫做了限製. 而且要編譯和運行需要訪問互聯網. 對於一些更複製的實驗, 你可能需要在自己的電腦上運行程序. 幸運的是下載Go的過程很簡單, 從 golang.org 下載應該不超過幾分鐘, 然後就可以在自己電腦上編寫和運行Go程序了.</p>
|
||||
<p>Go是一個開源項目, 你可以 在 <a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 閲讀標準庫中任意函數和類型的代碼, 和下載的代碼完全一致. 這樣可以知道很多函數是如何工作的, 挖掘一些答案的細節, 或者僅僅是欣賞 專業的Go代碼.</p>
|
||||
<p>最佳的幫助信息來自Go語言的官方網站,<a href="https://golang.org" target="_blank">https://golang.org</a> ,它提供了完善的參考文檔,包括編程語言規范和標準庫等諸多權威的幫助信息。同時也包含了如何編寫更地道的Go程序的基本敎程,還有各種各樣的在線文本資源和視頻資源,它們是本書最有價值的補充。Go語言的官方博客 <a href="https://blog.golang.org" target="_blank">https://blog.golang.org</a> 會不定期發布一些Go語言最好的實踐文章,包括當前語言的發展狀態、未來的計劃、會議報告和Go語言相關的各種會議的主題等信息(譯註: <a href="http://talks.golang.org/" target="_blank">http://talks.golang.org/</a> 包含了官方收録的各種報告的講稿)。</p>
|
||||
<p>在線訪問的一個有價值的地方是可以從web頁面運行Go語言的程序(而紙質書則沒有這麽便利了)。這個功能由來自 <a href="https://play.golang.org" target="_blank">https://play.golang.org</a> 的 Go Playground 提供,併且可以方便地嵌入到其他頁面中,例如 <a href="https://golang.org" target="_blank">https://golang.org</a> 的主頁,或 godoc 提供的文檔頁面中。</p>
|
||||
<p>Playground可以簡單的通過執行一個小程序來測試對語法、語義和對程序庫的理解,類似其他很多語言提供的REPL卽時運行的工具。同時它可以生成對應的url,非常適合共享Go語言代碼片段,滙報bug或提供反饋意見等。</p>
|
||||
<p>基於 Playground 構建的 Go Tour,<a href="https://tour.golang.org" target="_blank">https://tour.golang.org</a> ,是一個繫列的Go語言入門敎程,它包含了諸多基本概念和結構相關的併可在線運行的互動小程序。</p>
|
||||
<p>當然,Playground 和 Tour 也有一些限製,它們隻能導入標準庫,而且因爲安全的原因對一些網絡庫做了限製。如果要在編譯和運行時需要訪問互聯網,對於一些更複製的實驗,你可能需要在自己的電腦上構建併運行程序。幸運的是下載Go語言的過程很簡單,從 <a href="https://golang.org" target="_blank">https://golang.org</a> 下載安裝包應該不超過幾分鐘(譯註:感謝偉大的長城,讓大陸的Gopher們都學會了自己打洞的基本生活技能,下載時間可能會因爲洞的大小等因素從幾分鐘到幾天或更久),然後就可以在自己電腦上編寫和運行Go程序了。</p>
|
||||
<p>Go語言是一個開源項目,你可以在 <a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 閲讀標準庫中任意函數和類型的實現代碼,和下載安裝包的代碼完全一致。這樣你可以知道很多函數是如何工作的, 通過挖掘找齣一些答案的細節,或者僅僅是齣於欣賞專業級Go代碼。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="0.5" data-chapter-title="致謝" data-filepath="ch0/ch0-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="0.5" data-chapter-title="致謝" data-filepath="ch0/ch0-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,22 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="致謝">致謝</h2>
|
||||
<p>Rob Pike 和 Russ Cox, 以及其他很多Go糰隊的覈心成員多次仔細閲讀了本書的手稿,
|
||||
他們對本書的組織結構和表述用詞等給齣了很多寶貴的建議. 在準備日本翻譯的時候,
|
||||
Yoshiki Shibata 更是仔細地審閲了本書的每個部分, 及時發現了諸多英文和代碼的錯誤.
|
||||
我們非常感謝本書的審閲者, 併感謝對本書給齣了重要的建議的 Brian Goetz, Corey Kosak,
|
||||
Arnold Robbins, Josh Bleecher Snyder 和 Peter Weinberger 等人.</p>
|
||||
<p>我們感謝 Sameer Ajmani, Ittai Balaban, David Crawshaw, Billy Donohue, Jonathan Feinberg, Andrew Gerrand,
|
||||
Robert Griesemer, John Linderman, Minux Ma, Bryan Mills, Bala Natarajan, Cosmos Nicolaou, Paul Staniforth, Nigel Tao
|
||||
以及 Howard Trickey 給齣的許多有用的建議.
|
||||
我們還要感謝 David Brailsford 和 Raph Levien 關於類型設置的建議.</p>
|
||||
<p>我們來自 Addison-Wesley 的編輯 Greg Doench, 從最初就得到了越來越多的幫助.
|
||||
來自AW生産糰隊的 John Fuller, Dayna Isley, Julie Nahil, Chuti Prasertsith, 和 Barbara Wood,
|
||||
感謝你們的熱心幫助.</p>
|
||||
<p>Alan Donovan 特别感謝: Sameer Ajmani, Chris Demetriou, Walt Drummond 和 Google的Reid Tatge 允許他有充裕的時間去寫本書;
|
||||
感謝 Stephen Donovan 的建議和始終如一的鼓勵, 以及他的妻子 Leila Kazemi 沒有讓他爲了家庭瑣事而分心, 併熱情堅定地支持這個項目.</p>
|
||||
<p>Brian Kernighan特别感謝: 朋友和同事的耐心和寬容他, 讓他慢慢地梳理本身的寫作思路.
|
||||
同時感謝他的妻子 Meg 和其他很多朋友對他寫作事業的支持.</p>
|
||||
<p>Rob Pike和Russ Cox,以及很多其他Go糰隊的覈心成員多次仔細閲讀了本書的手稿,他們對本書的組織結構和表述用詞等給齣了很多寶貴的建議。在準備日文版翻譯的時候,Yoshiki Shibata更是仔細地審閲了本書的每個部分,及時發現了諸多英文和代碼的錯誤。我們非常感謝本書的每一位審閲者,併感謝對本書給齣了重要的建議的Brian Goetz、Corey Kosak、Arnold Robbins、Josh Bleecher Snyder和Peter Weinberger等人。</p>
|
||||
<p>我們還感謝Sameer Ajmani、Ittai Balaban、David Crawshaw、Billy Donohue、Jonathan Feinberg、Andrew Gerrand、Robert Griesemer、John Linderman、Minux Ma(譯註:中國人,Go糰隊成員。)、Bryan Mills、Bala Natarajan、Cosmos Nicolaou、Paul Staniforth、Nigel Tao(譯註:好像是陶哲軒的兄弟)以及Howard Trickey給齣的許多有價值的建議。我們還要感謝David Brailsford和Raph Levien關於類型設置的建議。</p>
|
||||
<p>我們的來自Addison-Wesley的編輯Greg Doench收到了很多幫助,從最開始就得到了越來越多的幫助。來自AW生産糰隊的John Fuller、Dayna Isley、Julie Nahil、Chuti Prasertsith到Barbara Wood,感謝你們的熱心幫助。</p>
|
||||
<p>Alan Donovan特别感謝:Sameer Ajmani、Chris Demetriou、Walt Drummond和Google公司的Reid Tatge允許他有充裕的時間去寫本書;感謝Stephen Donovan的建議和始終如一的鼓勵,以及他的妻子Leila Kazemi併沒有讓他爲了家庭瑣事而分心,併熱情堅定地支持這個項目。</p>
|
||||
<p>Brian Kernighan特别感謝:朋友和同事對他的耐心和寬容,讓他慢慢地梳理本書的寫作思路。同時感謝他的妻子Meg和其他很多朋友對他寫作事業的支持。</p>
|
||||
<p>2015年 10月 於 紐約</p>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.1" data-chapter-title="Hello, World" data-filepath="ch1/ch1-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.1" data-chapter-title="Hello, World" data-filepath="ch1/ch1-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,41 +2024,42 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="11-hello-world">1.1. Hello, World</h2>
|
||||
<p>我們以1978年,c語言歷史上經典的hello world案例來開始吧。C語言對Go語言的設計産生了很多影響。用這個例子,我們來講解一些Go語言的覈心特性:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">//gopl.io/ch1/helloworld</span>
|
||||
<p>我們以1978年齣版的C語言聖經《The C Programming Language》中經典的“hello world”案例來開始吧(譯註:本書作者之一Brian W. Kernighan也是C語言聖經一書的作者)。C語言對Go語言的設計産生了很多影響。用這個例子,我們來講解一些Go語言的覈心特性:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/helloworld
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Println(<span class="hljs-string">"Hello, BF"</span>)
|
||||
fmt.Println(<span class="hljs-string">"Hello, 世界"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>Go是一門編譯型語言,Go的工具鏈將源代碼和其依賴一起打包,生成機器的本地指令(譯註:靜態編譯)。Go語言提供的工具可以通過go下的一繫列子命令來調用。最簡單的一個子命令就是run。這個命令會將一個或多個以.go結束的源文件,和關聯庫鏈接到一起,然後運行最終的可執行文件。(本書將用$表示命令行的提示符)</p>
|
||||
<p>Go是一門編譯型語言,Go語言的工具鏈將源代碼和其依賴一起打包,生成機器的本地指令(譯註:靜態編譯)。Go語言提供的工具可以通過go命令下的一繫列子命令來調用。最簡單的一個子命令就是run。這個命令會將一個或多個文件名以.go結尾的源文件,和關聯庫鏈接到一起,然後運行最終的可執行文件。(本書將用$表示命令行的提示符。)</p>
|
||||
<pre><code>$ go run helloworld.go
|
||||
</code></pre><p>毫無意外,這個命令會輸齣:</p>
|
||||
<pre><code>Hello, BF
|
||||
</code></pre><p>Go原生支持Unicode,所以你可以用Go處理世界上的任何語言。</p>
|
||||
<pre><code>Hello, 世界
|
||||
</code></pre><p>Go語言原生支持Unicode標準,所以你可以用Go語言處理世界上的任何自然語言。</p>
|
||||
<p>如果你希望自己的程序不隻是簡單的一次性實驗,那麽你一定會希望能夠編譯這個程序,併且能夠將編譯結果保存下來以備將來之用。這個可以用build子命令來實現:</p>
|
||||
<pre><code>$ go build helloworld.go
|
||||
</code></pre><p>這會創建一個名爲helloworld的可執行的二進製文件,之後你可以在任何時間去運行這個二進製文件,不需要其它的任何處理(譯註:因爲是靜態編譯,所以也不用擔心在繫統庫更新的時候衝突,幸福感滿滿)。</p>
|
||||
<p>下面是運行我們的編譯結果樣例:</p>
|
||||
</code></pre><p>這會創建一個名爲helloworld的可執行的二進製文件(譯註:在Windows繫統下生成的可執行文件是helloworld.exe,增加了.exe後綴名),之後你可以在任何時間去運行這個二進製文件,不需要其它的任何處理(譯註:因爲是靜態編譯,所以也不用擔心在繫統庫更新的時候衝突,幸福感滿滿)。</p>
|
||||
<p>下面是運行我們的編譯結果樣例(譯註:在Windows繫統下在命令行直接輸入helloworld.exe命令運行):</p>
|
||||
<pre><code>$ ./helloworld
|
||||
Hello, BF
|
||||
</code></pre><p>本書中我們所有的例子都做了一個特殊標記,你可以通過這些標記在gopl.io在線網站上找到這些樣例代碼,比如這個 gopl.io/ch1/helloworld</p>
|
||||
<p>如果你執行go get gopl.io/ch1/helloworld,go能夠自己從網上穫取到這些代碼,併且將這些代碼放到對應的目録中。更詳細的介紹在2.6和10.7章節中。</p>
|
||||
<p>我們來討論一下程序本身。Go的代碼是用package來組織的,package的概念和你知道的其它語言里的libraries或者modules比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明開頭的,比如我們的例子里就是package main。這行聲明表示該文件是屬於哪一個package,緊跟着是一繫列import的package名,表示這個文件中引入的package。再之後是本文件本身的代碼</p>
|
||||
<p>Go的標準庫已經提供了100多個package,用來完成一門程序語言的一些基本任務,比如輸入、輸齣、排序或者字符串/文本處理。比如fmt這個package,就包括接收輸入、格式化輸齣的各種函數。Println是其中的一個函數,可以用這個函數來打印一個或多個值,該函數會將這些參數用空格隔開進行輸齣,併在輸齣完畢之後在行末加上一個換行符。</p>
|
||||
<p>package main比較特殊。這個package里會定義一個獨立的程序,這個程序是可以運行的,而不是像其它package一樣的library。在main這個package里,main函數也是一個特殊的函數,這是我們整個程序的入口(譯註:其實c繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了,main函數一般完成的工作是調用其它packge里的函數來完成自己的工作,比如fmt.Println。</p>
|
||||
<p>我們必鬚告訴編譯器如果要正確地執行這個源文件,需要用到哪些package,這就是import在這個文件里扮演的角色。上述的hello world隻用到了一個其它的package,就是fmt。一般情況下,需要import的package不隻一個。</p>
|
||||
<p>也正是因爲go語言必鬚引入所有用到的package的原則,假如你沒有在代碼里import需要用到的package,程序將無法編譯通過,當你import了沒有用到的package,也會無法編譯通過(譯註:爭議特性之一)。</p>
|
||||
<p>import聲明必鬚跟在文件的package聲明之後。在import之後,則是各種方法、變量、常量、類型的聲明(分别用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序併沒有什麽規定,可以隨便(譯註:最好還是定一下規范)。我們例子里的程序比較簡單,隻包含了一個函數。併且在該函數里也隻調用了一個其它函數。爲了節省空間,有些時候的例子我們會省略package和import聲明,但是讀者需要註意這些聲明是一定要包含在源文件里的。</p>
|
||||
<p>一個函數的聲明包含func這個關鍵字、函數名、參數列表(我們例子里的main函數是空)、返迴結果列表(這里的例子也是空)以及包含在大括號里的函數體。關於函數的更詳細描述在第五章。</p>
|
||||
<p>Go是一門不需要分號作爲語句或者聲明結束的語言,除非要在一行中將多個語句、聲明隔開。然而在編譯時,編譯器會主動在一些特定的符號(譯註:比如行末是,一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪里加分號合適是取決於Go的代碼的。例如:在Go語言中的函數聲明和 { 必鬚在同一行,而在x + y的表達式中,在+號後換行可以,但是在+號前換行則會有問題。</p>
|
||||
<p>Go語言在代碼格式上采取了很強硬的態度。gofmt工具會將你的代碼格式化爲標準格式,併且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt。如果不指定package,則默認對當前目録下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無盡的無意義的撕逼。當然了,也可以避免由於代碼格式導致的邏輯上的歧義。</p>
|
||||
Hello, 世界
|
||||
</code></pre><p>本書中我們所有的例子都做了一個特殊標記,你可以通過這些標記在 <a href="http://gopl.io" target="_blank">http://gopl.io</a> 在線網站上找到這些樣例代碼,比如這個</p>
|
||||
<pre><code>gopl.io/ch1/helloworld
|
||||
</code></pre><p>如果你執行 <code>go get gopl.io/ch1/helloworld</code> 命令,go命令能夠自己從網上穫取到這些代碼(譯註:需要先安裝Git或Hg之類的版本管理工具,併將對應的命令添加到PATH環境變量中),併且將這些代碼放到對應的目録中(譯註:序言已經提及,需要先設置好GOPATH環境變量,下載的代碼會放在 $GOPATH/src/gopl.io/ch1/helloworld 目録)。更詳細的介紹在2.6和10.7章節中。</p>
|
||||
<p>我們來討論一下程序本身。Go語言的代碼是通過package來組織的,package的概念和你知道的其它語言里的libraries或者modules概念比較類似。一個package會包含一個或多個.go結束的源代碼文件。每一個源文件都是以一個package xxx的聲明語句開頭的,比如我們的例子里就是package main。這行聲明語句表示該文件是屬於哪一個package,緊跟着是一繫列import的package名,表示這個文件中引入的package。再之後是本文件本身的代碼。</p>
|
||||
<p>Go的標準庫已經提供了100多個package,用來完成一門程序語言的一些常見的基本任務,比如輸入、輸齣、排序或者字符串/文本處理。比如fmt這個package,就包括接收輸入、格式化輸齣的各種函數。Println是其中的一個常用的函數,可以用這個函數來打印一個或多個值,該函數會將這些參數用空格隔開進行輸齣,併在輸齣完畢之後在行末加上一個換行符。</p>
|
||||
<p>package main是一個比較特殊的package。這個package里會定義一個獨立的程序,這個程序是可以運行的,而不是像其它package一樣對應一個library。在main這個package里,main函數也是一個特殊的函數,這是我們整個程序的入口(譯註:其實C繫語言差不多都是這樣)。main函數所做的事情就是我們程序做的事情。當然了,main函數一般是通過是調用其它packge里的函數來完成自己的工作,比如fmt.Println。</p>
|
||||
<p>我們必鬚告訴編譯器如何要正確地執行這個源文件,需要用到哪些package,這就是import在這個文件里扮演的角色。上述的hello world例子隻用到了一個其它的package,就是fmt。一般情況下,需要import的package可能不隻一個。</p>
|
||||
<p>這也正是因爲go語言必鬚引入所有要用到的package的原則,假如你沒有在代碼里import需要用到的package,程序將無法編譯通過,同時當你import了沒有用到的package,也會無法編譯通過(譯註:Go語言編譯過程沒有警告信息,爭議特性之一)。</p>
|
||||
<p>import聲明必鬚跟在文件的package聲明之後。在import語句之後,則是各種方法、變量、常量、類型的聲明語句(分别用關鍵字func, var, const, type來進行定義)。這些內容的聲明順序併沒有什麽規定,可以隨便調整順序(譯註:最好還是定一下規范)。我們例子里的程序比較簡單,隻包含了一個函數。併且在該函數里也隻調用了一個其它函數。爲了節省空間,有些時候的例子我們會省略package和import聲明,但是讀者需要註意這些聲明是一定要包含在源文件里的。</p>
|
||||
<p>一個函數的聲明包含func這個關鍵字、函數名、參數列表、返迴結果列表(我們例子里的main函數參數列表和返迴值都是空的)以及包含在大括號里的函數體。關於函數的更詳細描述在第五章。</p>
|
||||
<p>Go語言是一門不需要分號作爲語句或者聲明結束的語言,除非要在一行中將多個語句、聲明隔開。然而在編譯時,編譯器會主動在一些特定的符號(譯註:比如行末是,一個標識符、一個整數、浮點數、虛數、字符或字符串文字、關鍵字break、continue、fallthrough或return中的一個、運算符和分隔符++、--、)、]或}中的一個) 後添加分號,所以在哪里加分號合適是取決於Go語言代碼的。例如:在Go語言中的函數聲明和 { 大括號必鬚在同一行,而在x + y這樣的表達式中,在+號後換行可以,但是在+號前換行則會有問題(譯註:以+結尾的話不會被插入分號分隔符,但是以x結尾的話則會被分號分隔符,從而導致編譯錯誤)。</p>
|
||||
<p>Go語言在代碼格式上采取了很強硬的態度。gofmt工具會將你的代碼格式化爲標準格式(譯註:這個格式化工具沒有任何可以調整代碼格式的參數,Go語言就是這麽任性),併且go工具中的fmt子命令會自動對特定package下的所有.go源文件應用gofmt工具格式化。如果不指定package,則默認對當前目録下的源文件進行格式化。本書中的所有代碼已經是執行過gofmt後的標準格式代碼。你應該在自己的代碼上也執行這種格式化。規定一種標準的代碼格式可以規避掉無盡的無意義的撕逼(譯註:也導致了Go語言的TIOBE排名較低,因爲缺少撕逼的話題)。當然了,這可以避免由於代碼格式導致的邏輯上的歧義。</p>
|
||||
<p>很多文本編輯器都可以設置爲保存文件時自動執行gofmt,所以你的源代碼應該總是會被格式化。這里還有一個相關的工具,goimports,會自動地添加你代碼里需要用到的import聲明以及需要移除的import聲明。這個工具併沒有包含在標準的分發包中,然而你可以自行安裝:</p>
|
||||
<pre><code>$ go get golang.org/x/tools/cmd/goimports
|
||||
</code></pre><p>對於大多數用戶來説,下載、build package、運行測試用例、顯示go的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。</p>
|
||||
</code></pre><p>對於大多數用戶來説,下載、build package、運行測試用例、顯示Go語言的文檔等等常用功能都是可以用go的工具來實現的。這些工具的詳細介紹我們會在10.7節中提到。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.2" data-chapter-title="命令行參數" data-filepath="ch1/ch1-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.2" data-chapter-title="命令行參數" data-filepath="ch1/ch1-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2025,49 +2025,51 @@
|
||||
|
||||
<h2 id="12-命令行參數">1.2. 命令行參數</h2>
|
||||
<p>大多數的程序都是處理輸入,産生輸齣;這也正是“計算”的定義。但是一個程序要如何穫取輸入呢?一些程序會生成自己的數據,但通常情況下,輸入都來自於程序外部:比如文件、網絡連接、其它程序的輸齣、用戶的鍵盤、命令行的參數或其它類似輸入源。下面幾個例子會討論其中的一些輸入類型,首先是命令行參數。</p>
|
||||
<p>os這個package提供了操作繫統無關(跨平颱)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以用一個叫os包中的Args這個變量來穫取;在外部需要使用該變量時,需要用os.Args來訪問。</p>
|
||||
<p>os.Args這個變量是一個字符串(string)的slice,slice在go語言里是一個基礎的數據結構,之後我們很快會提到。現在可以先把slice當一個簡單的元素序列,可以用類似s[i]的下標訪問形式穫取其內容,併且可以用形如s[m:n]的形式來穫取到一個slice的子集(譯註:和python里的差不多)。其長度可以用len(s)函數來穫取。和其它大多數語言差不多,go語言里的這種索引形式也采用了開區間,包括m~n的第一個元素,但不包括最後那個元素(譯註:比如a = [1, 2, 3, 4, 5], a[0: 3] =[1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的邏輯。比如s[m:n]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。</p>
|
||||
<p>os.Args的第一個元素,卽os.Args[0]是命令行執行時的命令本身;其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式,s[m:n]會返迴第m到第n-1個元素,所以下一個例子里需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]里的m和n,那麽默認這個表達式會填入0:len(s),所以這里我們還可以省略掉n,寫os.Args[1:]。</p>
|
||||
<p>下面是一個Unix里echo命令的實現,這個命令會在單行內打印齣命令行參數。這個程序import了兩個package,併且用括號把這兩個package包了起來,這是分别import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麽問題,隻是一般爲了方便我們都會像下面這樣來導入多個package。我們自己寫的導入順序併不重要,因爲gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)</p>
|
||||
<p>os這個package提供了操作繫統無關(跨平颱)的,與繫統交互的一些函數和相關的變量,運行時程序的命令行參數可以通過os包中一個叫Args的這個變量來穫取;當在os包外部使用該變量時,需要用os.Args來訪問。</p>
|
||||
<p>os.Args這個變量是一個字符串(string)的slice(譯註:slice和Python語言中的切片類似,是一個簡版的動態數組),slice在Go語言里是一個基礎的數據結構,之後我們很快會提到。現在可以先把slice當一個簡單的元素序列,可以用類似s[i]的下標訪問形式穫取其內容,併且可以用形如s[m:n]的形式來穫取到一個slice的子集(譯註:和python里的語法差不多)。其長度可以用len(s)函數來穫取。和其它大多數編程語言類似,Go語言里的這種索引形式也采用了左閉右開區間,包括m~n的第一個元素,但不包括最後那個元素(譯註:比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最後一個元素)。這樣可以簡化我們的處理邏輯。比如s[m:n]這個slice,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。</p>
|
||||
<p>os.Args的第一個元素,卽os.Args[0]是命令行執行時的命令本身;其它的元素則是執行該命令時傳給這個程序的參數。前面提到的切片表達式,s[m:n]會返迴第m到第n-1個元素,所以下一個例子里需要用到的os.Args[1:len(os.Args)]卽是除了命令本身外的所有傳入參數。如果我們省略s[m:n]里的m和n,那麽默認這個表達式會填入0:len(s),所以這里我們還可以省略掉n,寫成os.Args[1:]。</p>
|
||||
<p>下面是一個Unix里echo命令的實現,這個命令會在單行內打印齣命令行參數。這個程序import了兩個package,併且用括號把這兩個package包了起來,這是分别import各個package聲明的簡化寫法。當然了你分開來寫import也沒有什麽問題,隻是一般爲了方便我們都會像下面這樣來導入多個package。我們自己寫的導入順序併不重要,因爲gofmt工具會幫助我們按照字母順序來排列好這些導入包名。(本書中如果一個例子有多種版本時,我們會用編號標記齣來)</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo1
|
||||
<span class="hljs-comment">// Echo1 prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-string">"os"</span>
|
||||
)
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
<span class="hljs-keyword">var</span> s, sep <span class="hljs-typename">string</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i < <span class="hljs-built_in">len</span>(os.Args); i++ {
|
||||
s += sep + os.Args[i]
|
||||
sep = <span class="hljs-string">" "</span>
|
||||
}
|
||||
fmt.Println(s)
|
||||
<span class="hljs-keyword">var</span> s, sep <span class="hljs-typename">string</span>
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i < <span class="hljs-built_in">len</span>(os.Args); i++ {
|
||||
s += sep + os.Args[i]
|
||||
sep = <span class="hljs-string">" "</span>
|
||||
}
|
||||
fmt.Println(s)
|
||||
}
|
||||
</code></pre>
|
||||
<p>Go里的註釋是以//來表示。//後的內容一直到行末都是這條註釋的一部分,併且這些註釋會被編譯器忽略。</p>
|
||||
<p>Go語言里的註釋是以//來表示。//之後的內容一直到行末都是這條註釋的一部分,併且這些註釋會被編譯器忽略。</p>
|
||||
<p>按照慣例,我們會在每一個package前面放上這個package的詳盡的註釋對其進行説明;對於一個main package來説,一般這段評論會包含幾句話來説明這個項目/程序整體是做什麽用的。</p>
|
||||
<p>var關鍵字用來做變量聲明。這里聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話,Go會隱式地給這些未初始化的變量賦予對應其類型的零值,比如數值類型就是0,字符串類型就是“”空字符串。在這個例子里的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。</p>
|
||||
<p>對於數字類型,Go語言提供了常規的數值計算和邏輯運算符。而對於string類型,+號表示字符串的連接(譯註:和C++或者js是一樣的)。所以下面這個表達式:</p>
|
||||
<p>var關鍵字用來做變量聲明。這個程序聲明了s和sep兩個string變量。變量可以在聲明期間直接進行初始化。如果沒有顯式地初始化的話,Go語言會隱式地給這些未初始化的變量賦予對應其類型的零值,比如數值類型就是0,字符串類型就是空字符串“”。在這個例子里的s和sep被隱式地賦值爲了空字符串。在第2章中我們會更詳細地講解變量和聲明。</p>
|
||||
<p>對於數字類型,Go語言提供了常規的數值計算和邏輯運算符。而對於string類型,+號表示字符串的連接(譯註:和C++或者js是一樣的)。所以下面這個表達式:</p>
|
||||
<pre><code class="lang-go">sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>表示將sep字符串和os.Args[i]字符串進行連接。我們在程序里用的另外一個表達式:</p>
|
||||
<pre><code class="lang-go">s += sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>會將sep與os.Args[i]連接,然後再將得到的結果與s進行連接,這種方式和下面的表達是等價的:</p>
|
||||
<p>會將sep與os.Args[i]連接,然後再將得到的結果與s進行連接併賦值運給s,這種方式和下面的表達是等價的:</p>
|
||||
<pre><code class="lang-go">s = s + sep + os.Args[i]
|
||||
</code></pre>
|
||||
<p>運算符+=是一個賦值運算符(assignment operator),每一種數值和邏輯運算符,例如*或者+都有其對應的賦值運算符。</p>
|
||||
<p>echo程序可以每循環一次輸齣一個參數,不過我們這里的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串,而之後循環每次都會被在末尾添加一段字符串;第一次迭代之後,一個空格會被插入到字符串末尾,所以每插入一個新值,都會和前一個中間有一個空格隔開。這是一種非線性的操作,當我們的參數數量變得龐大的時候(當然不是説這里的echo,一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版,來應對這里説到的運行效率低下。</p>
|
||||
<p>在for循環中,我們用到了i來做下標索引,可以看到我們用了:=符號來給i進行初始化和賦值,這是var xxx=yyy的一種簡寫形式,Go會根據等號右邊的值的類型自動判斷左邊的值類型,下一章會對這一點進行詳細説明。</p>
|
||||
<p>自增表達式i++會爲i加上1;這個i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在go語言里是語句,而不像C繫的其它語言里是表達式。所以在Go語言里j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。</p>
|
||||
<p>echo程序可以每循環一次輸齣一個參數,不過我們這里的版本是不斷地將其結果連接到一個字符串的末尾。s這個字符串在聲明的時候是一個空字符串,而之後循環每次都會被在末尾添加一段字符串;第一次迭代之後,一個空格會被插入到字符串末尾,所以每插入一個新值,都會和前一個中間有一個空格隔開。這是一種非線性的操作,當我們的參數數量變得龐大的時候(當然不是説這里的echo,一般echo也不會有太多參數)其運行開銷也會變得龐大。下面我們會介紹一繫列的echo改進版,來應對這里説到的運行效率低下。</p>
|
||||
<p>在for循環中,我們用到了i來做下標索引,可以看到我們用了:=符號來給i進行初始化和賦值,這是var xxx=yyy的一種簡寫形式,Go語言會根據等號右邊的值的類型自動判斷左邊的值類型,下一章會對這一點進行詳細説明。</p>
|
||||
<p>自增表達式i++會爲i加上1;這和i += 1以及i = i + 1都是等價的。對應的還有i--是給i減去1。這些在Go語言里是語句,而不像C繫的其它語言里是表達式。所以在Go語言里j = i++是非法的,而且++和--都隻能放在變量名後面,因此--i也是非法的。</p>
|
||||
<p>在Go語言里隻有for循環一種循環。當然了爲了滿足需求,Go的for循環有很多種形式,下面是其中的一種:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">for</span> initialization; condition; post {
|
||||
<span class="hljs-comment">// zero or more statements</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>這里需要註意,for循環的兩邊是不需要像其它語言一樣寫括號的。併且左大括號需要和for語句在同一行。</p>
|
||||
<p>initialization部分是可選的,如果你寫了這部分的話,在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句,也就是説是一個簡短的變量聲明,一個賦值語句,或是一個函數調用。condition部分必鬚是一個結果爲boolean值的表達式,在每次循環之前,語言都會檢査當前是否滿足這個條件,如果不滿足的話便會結束循環;post部分的語句則是在每次循環結束之後被執行,之後conditon部分會在下一次執行前再被執行,依此往複。當condition條件里的判斷結果變爲false之後,循環卽結束。</p>
|
||||
<p>initialization部分是可選的,如果你寫了這部分的話,在for循環之前這部分的邏輯會被執行。需要註意的是這部分必鬚是一個簡單的語句,也就是説是一個簡短的變量聲明,一個賦值語句,或是一個函數調用。condition部分必鬚是一個結果爲boolean值的表達式,在每次循環之前,語言都會檢査當前是否滿足這個條件,如果不滿足的話便會結束循環;post部分的語句則是在每次循環迭代結束之後被執行,之後conditon部分會在下一次執行前再被執行,依此往複。當condition條件里的判斷結果變爲false之後,循環卽結束。</p>
|
||||
<p>上面提到是for循環里的三個部分都是可以被省略的,如果你把initialization和post部分都省略的話,那麽連中間隔離他們的分號也是可以被省略的,比如下面這種for循環,就和傳統的while循環是一樣的:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-comment">// a traditional "while" loop</span>
|
||||
<span class="hljs-keyword">for</span> condition {
|
||||
@@ -2080,8 +2082,8 @@
|
||||
<span class="hljs-comment">// ...</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在無限循環中,你還是可以靠break或者return來終止掉循環。</p>
|
||||
<p>如果你的遍歷對象是string或者slice里的值的話,還有另外一種循環的寫法,我們來看看另一個版本的echo:</p>
|
||||
<p>在無限循環中,你還是可以靠break或者return語句來終止掉循環。</p>
|
||||
<p>如果你的遍歷對象是string或者slice類型值的話,還有另外一種循環的寫法,我們來看看另一個版本的echo:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo2
|
||||
<span class="hljs-comment">// Echo2 prints its command-line arguments.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2099,18 +2101,17 @@
|
||||
fmt.Println(s)
|
||||
}
|
||||
</code></pre>
|
||||
<p>每一次循環迭代,range都會返迴一對結果;當前迭代的下標以及在該下標處的元素的值。在這個例子里,我們不需要這個下標,但是因爲range的處理要求我們必鬚要同時處理下標和值。我們可以在這里聲明一個接收index的臨時變量來解決這個問題,但是go語言又不允許隻聲明而在後續代碼里不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。</p>
|
||||
<p>在Go語言中,應對這種情況的解決方法是用空白標識符,對,就是上面那個下劃線<em>。空白標識符可以在任何你接收自己不需要處理的值時使用。在這里,我們用他來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上面這樣來寫類似的os.Args遍歷,可以避免錯誤的下標引用。(這里可能有翻譯錯,附上原文)
|
||||
Most Go programmers would likely use range and </em> to write the echo program as above, since the indexing over os.Args is implicit, not explicit, and thus easier to get right.</p>
|
||||
<p>每一次循環迭代,range都會返迴一對結果;當前迭代的下標以及在該下標處的元素的值。在這個例子里,我們不需要這個下標,但是因爲range的處理要求我們必鬚要同時處理下標和值。我們可以在這里聲明一個接收index的臨時變量來解決這個問題,但是Go語言又不允許隻聲明而在後續代碼里不使用這個變量,如果你這樣做了編譯器會返迴一個編譯錯誤。</p>
|
||||
<p>在Go語言中,應對這種情況的解決方法是用空白標識符,對,就是上面那個下劃線_。空白標識符可以在任何你接收自己不需要處理的值時使用。在這里,我們用它來忽略掉range返迴的那個沒用的下標值。大多數的Go程序員都會像上面這樣來寫類似的os.Args遍歷,由於遍歷os.Args的下標索引是隱式自動生成的,可以避免因顯式更新索引導致的錯誤。</p>
|
||||
<p>上面這個版本將s和sep的聲明和初始化都放到了一起,但是我們可以等價地將聲明和賦值分開來寫,下面這些寫法都是等價的</p>
|
||||
<pre><code class="lang-go">s := <span class="hljs-string">""</span>
|
||||
<span class="hljs-keyword">var</span> s <span class="hljs-typename">string</span>
|
||||
<span class="hljs-keyword">var</span> s = <span class="hljs-string">""</span>
|
||||
<span class="hljs-keyword">var</span> s <span class="hljs-typename">string</span> = <span class="hljs-string">""</span>
|
||||
</code></pre>
|
||||
<p>那麽這些等價的形式應該怎麽做選擇呢?這里提供一些建議:第一種形式,最好隻用在一個函數內部,而package級别的變量,請不要使用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製,被初始化爲空字符串。第三種形式使用得很少,除非同時聲明多個變量。第四種形式會顯式地標明變量的類型,在多變量同時聲明時可以用到。實踐中你應該隻使用上面的前兩種形式,顯式地指定變量的類型,讓編譯器自己去初始化其值,或者直接用隱式初始化,表明初始值怎麽樣併不重要。</p>
|
||||
<p>像上面提到的,每次循環中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,併將老字符串連接起來的值賦予給它。而目標字符串的老字面值在得到新值以後就失去了用處,這些臨時值會被go的垃圾收集器榦掉。</p>
|
||||
<p>如果不斷連接的數據量很大,那麽上面這種操作就是成本非常高的操作。更簡單併且有效的一種方式是使用字符串的Join函數,像下面這樣:</p>
|
||||
<p>那麽這些等價的形式應該怎麽做選擇呢?這里提供一些建議:第一種形式,隻能用在一個函數內部,而package級别的變量,禁止用這樣的聲明方式。第二種形式依賴於string類型的內部初始化機製,被初始化爲空字符串。第三種形式使用得很少,除非同時聲明多個變量。第四種形式會顯式地標明變量的類型,在多變量同時聲明時可以用到。實踐中你應該隻使用上面的前兩種形式,顯式地指定變量的類型,讓編譯器自己去初始化其值,或者直接用隱式初始化,表明初始值怎麽樣併不重要。</p>
|
||||
<p>像上面提到的,每次循環迭代中字符串s都會得到一個新內容。+=語句會分配一個新的字符串,併將老字符串連接起來的值賦予給它。而目標字符串的老字面值在得到新值以後就失去了用處,這些臨時值會被Go語言的垃圾收集器榦掉。</p>
|
||||
<p>如果不斷連接的數據量很大,那麽上面這種操作就是成本非常高的操作。更簡單併且有效的一種方式是使用strings包提供的Join函數,像下面這樣:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/echo3
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
fmt.Println(strings.Join(os.Args[<span class="hljs-number">1</span>:], <span class="hljs-string">" "</span>))
|
||||
@@ -2119,12 +2120,11 @@ Most Go programmers would likely use range and </em> to write the echo program a
|
||||
<p>最後,如果我們對輸齣的格式也不是很關心,隻是想簡單地輸齣值得的話,還可以像下面這麽寫,Println函數會爲我們自動格式化輸齣。</p>
|
||||
<pre><code class="lang-go">fmt.Println(os.Args[<span class="hljs-number">1</span>:])
|
||||
</code></pre>
|
||||
<p>這個輸齣結果和前面的string.Join得到的結果很相似,隻是被自動地放到了一個括號里,對slice調用Println函數都會被打印成這樣形式的結果。</p>
|
||||
<p>下面是幾道練習題:</p>
|
||||
<pre><code>Exercise 1.1:脩改echo程序,使其能夠打印os.Args[0]。
|
||||
Exercise 1.2:脩改echo程序,使其打印value和index,每個value和index顯示一行。
|
||||
Exercise 1.3:上手實踐前面提到的strings.Join和直接Println,併觀察輸齣結果的區别。
|
||||
</code></pre>
|
||||
<p>這個輸齣結果和前面的string.Join得到的結果很相似,隻是被自動地放到了一個方括號里,對slice調用Println函數都會被打印成這樣形式的結果。</p>
|
||||
<p><strong>練習 1.1:</strong> 脩改echo程序,使其能夠打印os.Args[0]。</p>
|
||||
<p><strong>練習 1.2:</strong> 脩改echo程序,使其打印value和index,每個value和index顯示一行。</p>
|
||||
<p><strong>練習 1.3:</strong> 上手實踐前面提到的strings.Join和直接Println,併觀察輸齣結果的區别。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.3" data-chapter-title="査找重複的行" data-filepath="ch1/ch1-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.3" data-chapter-title="査找重複的行" data-filepath="ch1/ch1-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="13-査找重複的行">1.3. 査找重複的行</h2>
|
||||
<p>文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構:處理輸入的一個循環,在每一個輸入元素上執行計算處理,在處理的同時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本;這個程序的靈感來自於linux的uniq命令,我們的程序將會找到相鄰的重複的行。這個程序提供的模式可以很方便地被脩改來完成不同的需求。</p>
|
||||
<p>第一個版本的dup會輸齣標準輸入流中的齣現多次的行,在行內容前會有其齣現次數的計數。這個程序將引入if表達式,map內置數據結果和bufio的package。</p>
|
||||
<p>文件拷貝、文件打印、文件蒐索、文件排序、文件統計類的程序一般都會有比較相似的程序結構:一個處理輸入的循環,在每一個輸入元素上執行計算處理,在處理的同時或者處理完成之後進行結果輸齣。我們會展示一個叫dup程序的三個版本;這個程序的靈感來自於linux的uniq命令,我們的程序將會找到相鄰的重複的行。這個程序提供的模式可以很方便地被脩改來完成不同的需求。</p>
|
||||
<p>第一個版本的dup會輸齣標準輸入流中的齣現多次的行,在行內容前會有其齣現次數的計數。這個程序將引入if表達式,map內置數據結構和bufio的package。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup1
|
||||
<span class="hljs-comment">// Dup1 prints the text of each line that appears more than</span>
|
||||
<span class="hljs-comment">// once in the standard input, preceded by its count.</span>
|
||||
@@ -2051,31 +2051,31 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>和我們前面提到的for循環一樣,在if條件的兩邊,我們也不需要加括號,但是if表達式後的邏輯體的花括號是不能省略的。如果需要的話,像其它語言一樣,這個表達式也可以有else部分,這部分邏輯會在if中的條件結果爲false時被執行。</p>
|
||||
<p>map是go語言內置的key/value數據結構,這個數據結構能夠提供常數時間的存儲、穫取、測試操作。key可以是任意數據類型,隻要該類型能夠用==來進行比較,string是最常用的key類型。而value類型的范圍就更大了,基本上什麽類型都是可以的。這個例子中的key都是string類型,value用的是int類型。我們用內置make函數來創建一個空的map,當然了,make方法還可以有别的用處。在4.3章中我們還會對map進行更深度的討論。</p>
|
||||
<p>和我們前面提到的for循環一樣,在if條件的兩邊,我們也不需要加括號,但是if表達式後的邏輯體的花括號是不能省略的。如果需要的話,像其它編程語言一樣,這個if表達式也可以有else部分,這部分邏輯會在if中的條件結果爲false時被執行。</p>
|
||||
<p>map是Go語言內置的key/value型數據結構,這個數據結構能夠提供常數時間的存儲、穫取、測試操作。key可以是任意數據類型,隻要該類型能夠用==運算符來進行比較,string是最常用的key類型。而value類型的范圍就更大了,基本上什麽類型都是可以的。這個例子中的key都是string類型,value用的是int類型。我們用內置make函數來創建一個空的map,當然了,make方法還可以有别的用處。在4.3章中我們還會對map進行更深入的討論。</p>
|
||||
<p>dup程序每次讀取輸入的一行,這一行的內容會被當做一個map的key,而其value值會被+1。counts[input.Text()]++這個語句和下面的兩句是等價的:</p>
|
||||
<pre><code class="lang-go">line := input.Text()
|
||||
counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
</code></pre>
|
||||
<p>當然了,在這個例子里我們併不用擔心map在沒有當前的key時就對其進行++操作會有什麽問題,因爲go語言在碰到這種情況時,會自動將其初始化爲0,然後再進行操作。</p>
|
||||
<p>在這里我們又用了一個range的循環來打印結果,這次range是被用在map這個數據結果上。這一次的情況和上次比較類型,range會返迴兩個值,一個key和在map對應這個key的value。對map進行range循環時,其順序是不確定的,從實踐來看,很可能每次運行都會有不一樣的結果(譯註:這是go的設計者有意爲之的,因爲其底層實現不保證插入順序和遍歷順序一致,而希望程序員不要依賴遍歷時的順序,所以榦脆直接在遍歷的時候做了隨機化處理,醉了),來避免程序員在業務中依賴遍歷時的順序。</p>
|
||||
<p>當然了,在這個例子里我們併不用擔心map在沒有當前的key時就對其進行++操作會有什麽問題,因爲Go語言在碰到這種情況時,會自動將其初始化爲0,然後再進行操作。</p>
|
||||
<p>在這里我們又用了一個range的循環來打印結果,這次range是被用在map這個數據結構之上。這一次的情況和上次比較類似,range會返迴兩個值,一個key和在map對應這個key的value。對map進行range循環時,其迭代順序是不確定的,從實踐來看,很可能每次運行都會有不一樣的結果(譯註:這是Go語言的設計者有意爲之的,因爲其底層實現不保證插入順序和遍歷順序一致,也希望程序員不要依賴遍歷時的順序,所以榦脆直接在遍歷的時候做了隨機化處理,醉了。補充:好像説隨機序可以防止某種類型的攻擊,雖然不太明白,但是感覺還蠻厲害的),來避免程序員在業務中依賴遍歷時的順序。</p>
|
||||
<p>然後輪到我們例子中的bufio這個package了,這個package主要的目的是幫助我們更方便有效地處理程序的輸入和輸齣。而這個包最有用的一個特性就是其中的一個Scanner類型,用它可以簡單地接收輸入,或者把輸入打散成行或者單詞;這個類型通常是處理行形式的輸入最簡單的方法了。</p>
|
||||
<p>本程序中用了一個短變量聲明,來創建一個buffio.Scanner對象:</p>
|
||||
<pre><code>input := bufio.NewScanner(os.Stdin)
|
||||
</code></pre><p>scanner對象可以從程序的標準輸入中讀取內容。對input.Scanner的每一次調用都會調入一個新行,併且會自動將其行末的換行符去掉;其結果可以用input.Text()得到。Scan方法在讀到了新行的時候會返迴true,而在沒有新行被讀入時,會返迴false。</p>
|
||||
<p>例子中還有一個fmt.Printf,這個函數和C繫的其它語言里的那個printf函數差不多,都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約,每一個參數如果格式化是取決於在格式化字符串里齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d表示以一個整數的形式來打印一個變量,而%s,則表示以string形式來打印一個變量。</p>
|
||||
<p>Printf有一大堆這種轉換,Go程序員把這些叫做verb(動詞)。下面的表格列齣了常用的動詞,當然了不是全部,但基本也夠用了。</p>
|
||||
<p>例子中還有一個fmt.Printf,這個函數和C繫的其它語言里的那個printf函數差不多,都是格式化輸齣的方法。fmt.Printf的第一個參數卽是輸齣內容的格式規約,每一個參數如何格式化是取決於在格式化字符串里齣現的“轉換字符”,這個字符串是跟着%號後的一個字母。比如%d表示以一個整數的形式來打印一個變量,而%s,則表示以string形式來打印一個變量。</p>
|
||||
<p>Printf有一大堆這種轉換,Go語言程序員把這些叫做verb(動詞)。下面的表格列齣了常用的動詞,當然了不是全部,但基本也夠用了。</p>
|
||||
<pre><code>%d int變量
|
||||
%x, %o, %b 分别爲16進製,8進製,2進製形式的int
|
||||
%f, %g, %e 浮點數: 3.141593 3.141592653589793 3.141593e+00
|
||||
%t 布爾變量:true 或 false
|
||||
%c rune (Unicode code point),go語言里特有的Unicode字符類型
|
||||
%c rune (Unicode碼點),Go語言里特有的Unicode字符類型
|
||||
%s string
|
||||
%q quoted string "abc" or rune 'c'
|
||||
%q 帶雙引號的字符串 "abc" 或 帶單引號的 rune 'c'
|
||||
%v 會將任意變量以易讀的形式打印齣來
|
||||
%T 打印變量的類型
|
||||
%% 字符型百分比標誌(不確定) literal percent sign (no operand)
|
||||
</code></pre><p>dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例,用來格式化的函數都會在末尾以f字母結尾,比如log.Printf,fmt.Errorf,同時還有一繫列對應以ln結尾的函數,這些函數默認以%v來格式化他們的參數,併且會在輸齣結束後在最後自動加上一個換行符。</p>
|
||||
%% 字符型百分比標誌(%符號本身,沒有其他操作)
|
||||
</code></pre><p>dup1中的程序還包含了一個\t和\n的格式化字符串。在字符串中會以這些特殊的轉義字符來表示不可見字符。Printf默認不會在輸齣內容後加上換行符。按照慣例,用來格式化的函數都會在末尾以f字母結尾(譯註:f後綴對應format或fmt縮寫),比如log.Printf,fmt.Errorf,同時還有一繫列對應以ln結尾的函數(譯註:ln後綴對應line縮寫),這些函數默認以%v來格式化他們的參數,併且會在輸齣結束後在最後自動加上一個換行符。</p>
|
||||
<p>許多程序從標準輸入中讀取數據,像上面的例子那樣。除此之外,還可能從一繫列的文件中讀取。下一個dup程序就是從標準輸入中讀到一些文件名,用os.Open函數來打開每一個文件穫取內容的。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup2
|
||||
<span class="hljs-comment">// Dup2 prints the count and text of lines that appear more than once</span>
|
||||
@@ -2120,12 +2120,12 @@ counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>os.Open函數會返迴兩個值。第一個值是一個打開的文件類型(*os.File),這個對象在下面的程序中被Scanner讀取。</p>
|
||||
<p>os.Open返迴的第二個值是一個go內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言里的NULL)相等的話,説明文件被成功的打開了。之後文件被讀取,一直到文件的最後,Close函數關閉該文件,併釋放相應的占用一切資源。另一方面,如果err的值不是nil的話,那説明在打開文件的時候齣了某種錯誤。這種情況下,error類型的值會描述具體的問題。我們例子里的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件;continue語句會直接跳過之後的語句,直接開始執行下一次循環。</p>
|
||||
<p>我們在本書中早期的例子中做了比較詳盡的錯誤處理,當然了,在實際編碼過程中,像os.Open這類的函數是一定要檢査其返迴的error值的;爲了減少例子程序的代碼量,我們姑且簡化掉這些不太可能返迴錯誤的邏輯。後面的例子里我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。</p>
|
||||
<p>讀者可以再觀察一下上面的例子,我們的countLines函數是在其聲明之前就被調用了。在Go語言里,函數和包級别的變量可以以任意的順序被聲明,併不影響其被調用。(譯註:最好還是遵循一定的規范)</p>
|
||||
<p>再來講講map這個數據結構,map是用make函數創建的數據結構的一個引用。當一個map被作爲參數傳遞給一個函數時,函數接收到的是一份引用的拷貝,雖然本身併不是一個東西,但因爲他們指向的是同一塊數據對象(譯註:類似於C艹里的引用傳遞),所以你在函數里對map里的值進行脩改時,原始的map內的值也會改變。在我們的例子中,我們在countLines函數中插入到counts這個map里的值,在主函數中也是看得到的。</p>
|
||||
<p>上面這個版本的dup是以流的形式來處理輸入,併將其打散爲行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中,然後再把其分割爲多行,然後再去處理這些行內的數據。下面的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil這個包),這個函數會把一個指定名字的文件內容一次性調入,之後我們用strings.Split函數把文件分割爲多個子字符串,併存儲到slice結構中。(Split函數是strings.Join的逆函數,Join函數之前提到過)</p>
|
||||
<p>我們簡化了dup3這個程序。首先,他隻讀取命名的文件,而不去讀標準輸入,因爲ReadFile函數需要一個文件名參數。其次,我們將行計數邏輯移迴到了main函數,因爲現在這個邏輯隻有一個地方需要用到。</p>
|
||||
<p>os.Open返迴的第二個值是一個Go語言內置的error類型。如果這個error和內置值的nil(譯註:相當於其它語言里的NULL)相等的話,説明文件被成功的打開了。之後文件被讀取,一直到文件的最後,文件的Close方法關閉該文件,併釋放相應的占用一切資源。另一方面,如果err的值不是nil的話,那説明在打開文件的時候齣了某種錯誤。這種情況下,error類型的值會描述具體的問題。我們例子里的簡單錯誤處理會在標準錯誤流中用Fprintf和%v來格式化該錯誤字符串。然後繼續處理下一個文件;continue語句會直接跳過之後的語句,直接開始執行下一個循環迭代。</p>
|
||||
<p>我們在本書中早期的例子中做了比較詳盡的錯誤處理,當然了,在實際編碼過程中,像os.Open這類的函數是一定要檢査其返迴的error值的;爲了減少例子程序的代碼量,我們姑且簡化掉這些不太可能返迴錯誤的處理邏輯。後面的例子里我們會跳過錯誤檢査。在5.4節中我們會對錯誤處理做更詳細的闡述。</p>
|
||||
<p>讀者可以再觀察一下上面的例子,我們的countLines函數是在其聲明之前就被調用了。在Go語言里,函數和包級别的變量可以以任意的順序被聲明,併不影響其被調用。(譯註:最好還是遵循一定的規范)</p>
|
||||
<p>再來講講map這個數據結構,map是用make函數創建的數據結構的一個引用。當一個map被作爲參數傳遞給一個函數時,函數接收到的是一份引用的拷貝,雖然本身併不是一個東西,但因爲他們指向的是同一塊數據對象(譯註:類似於C++里的引用傳遞),所以你在函數里對map里的值進行脩改時,原始的map內的值也會改變。在我們的例子中,我們在countLines函數中插入到counts這個map里的值,在主函數中也是看得到的。</p>
|
||||
<p>上面這個版本的dup是以流的形式來處理輸入,併將其打散爲行。理論上這些程序也是可以以二進製形式來處理輸入的。我們也可以一次性的把整個輸入內容全部讀到內存中,然後再把其分割爲多行,然後再去處理這些行內的數據。下面的dup3這個例子就是以這種形式來進行操作的。這個例子引入了一個新函數ReadFile(從io/ioutil包提供),這個函數會把一個指定名字的文件內容一次性調入,之後我們用strings.Split函數把文件分割爲多個子字符串,併存儲到slice結構中。(Split函數是strings.Join的逆函數,Join函數之前提到過)</p>
|
||||
<p>我們簡化了dup3這個程序。首先,它隻讀取命名的文件,而不去讀標準輸入,因爲ReadFile函數需要一個文件名參數。其次,我們將行計數邏輯移迴到了main函數,因爲現在這個邏輯隻有一個地方需要用到。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/dup3
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
@@ -2155,10 +2155,10 @@ counts[line] = counts[line] + <span class="hljs-number">1</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>ReadFile函數返迴一個byte的slice,這個slice必鬚被轉換爲string,之後纔能夠用string.Split方法來進行處理。我們在3.5.4節中會更詳細地講解string和byte slice(字節數組)。</p>
|
||||
<p>ReadFile函數返迴一個byte的slice,這個slice必鬚被轉換爲string,之後纔能夠用string.Split方法來進行處理。我們在3.5.4節中會更詳細地講解string和byte slice(字節數組)。</p>
|
||||
<p>在更底層一些的地方,bufio.Scanner,ioutil.ReadFile和ioutil.WriteFile使用的是*os.File的Read和Write方法,不過一般程序員併不需要去直接了解到其底層實現細節,在bufio和io/ioutil包中提供的方法已經足夠好用。</p>
|
||||
<pre><code>Exercise 1.4: 脩改dup2,使其可以打印重複的行分别齣現在哪些文件。
|
||||
</code></pre>
|
||||
<p><strong>練習 1.4:</strong> 脩改dup2,使其可以打印重複的行分别齣現在哪些文件。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.4" data-chapter-title="GIF動畵" data-filepath="ch1/ch1-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.4" data-chapter-title="GIF動畵" data-filepath="ch1/ch1-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,15 +2024,16 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="14-gif動畵">1.4. GIF動畵</h2>
|
||||
<p>下面的程序會演示Go語言標準庫里的image這個package的用法,我們會用這個包來生成一繫列的bit-mapped圖,然後將這些圖片編碼爲一個GIF動畵。我們生成的圖形名字叫利薩如圖形(Lissajous figures),這種效果是在1960年代的老電影里齣現的一種視覺特效。他們是協振子在兩個緯度上振動所産生的麴線,比如兩個sin正絃波分别在x軸和y軸輸入會産生的麴線。圖1.1是這樣的一個例子:</p>
|
||||
<p>下面的程序會演示Go語言標準庫里的image這個package的用法,我們會用這個包來生成一繫列的bit-mapped圖,然後將這些圖片編碼爲一個GIF動畵。我們生成的圖形名字叫利薩如圖形(Lissajous figures),這種效果是在1960年代的老電影里齣現的一種視覺特效。它們是協振子在兩個緯度上振動所産生的麴線,比如兩個sin正絃波分别在x軸和y軸輸入會産生的麴線。圖1.1是這樣的一個例子:</p>
|
||||
<p><img src="../images/ch1-01.png" alt=""></p>
|
||||
<p>這段代碼里我們用了一些新的結構,包括const聲明,數據struct類型,複合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概念我們隻在這里簡單地説明一下,之後的章節會更詳細地講解。</p>
|
||||
<p>譯註:要看這個程序的結果,需要將標準輸齣重定向到一個GIF圖像文件(使用 <code>./lissajous > output.gif</code> 命令)。下面是GIF圖像動畵效果:</p>
|
||||
<p><img src="../images/ch1-01.gif" alt=""></p>
|
||||
<p>這段代碼里我們用了一些新的結構,包括const聲明,struct結構體類型,複合聲明。和我們舉的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概念我們隻在這里簡單地説明一下,之後的章節會更詳細地講解。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/lissajous
|
||||
|
||||
<span class="hljs-comment">// Lissajous generates GIF animations of random Lissajous figures.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"image"</span>
|
||||
<span class="hljs-string">"image/color"</span>
|
||||
@@ -2044,10 +2045,12 @@
|
||||
)
|
||||
|
||||
<span class="hljs-keyword">var</span> palette = []color.Color{color.White, color.Black}
|
||||
|
||||
<span class="hljs-keyword">const</span> (
|
||||
whiteIndex = <span class="hljs-number">0</span> <span class="hljs-comment">// first color in palette</span>
|
||||
blackIndex = <span class="hljs-number">1</span> <span class="hljs-comment">// next color in palette</span>
|
||||
)
|
||||
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
lissajous(os.Stdout)
|
||||
}
|
||||
@@ -2071,7 +2074,7 @@
|
||||
x := math.Sin(t)
|
||||
y := math.Sin(t*freq + phase)
|
||||
img.SetColorIndex(size+<span class="hljs-typename">int</span>(x*size+<span class="hljs-number">0.5</span>), size+<span class="hljs-typename">int</span>(y*size+<span class="hljs-number">0.5</span>),
|
||||
blackIndex)
|
||||
bla kIndex)
|
||||
}
|
||||
phase += <span class="hljs-number">0.1</span>
|
||||
anim.Delay = <span class="hljs-built_in">append</span>(anim.Delay, delay)
|
||||
@@ -2080,19 +2083,18 @@ blackIndex)
|
||||
gif.EncodeAll(out, &anim) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring encoding errors</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩個單詞),我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時,這個變量指向的是image/color包里的變量,同理gif.GIF是屬於image/gif包里的變量。</p>
|
||||
<p>這個程序里的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級别,所以這些常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數體內部,那麽這種常量就隻能在函數體內用。常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。</p>
|
||||
<p>[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們説的複合聲明(4.2和4.4.1節有説明)。這是實例化Go語言里的複合類型的一種寫法。這里的前者生成的是一個slice,後者生成的是一個struct。</p>
|
||||
<p>gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合,不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量,併且其內部變量LoopCount字段會被設置爲nframes;而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問,就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。</p>
|
||||
<p>lissajous函數內部有兩層嵌太的for循環。外層循環會循環64次,每一次都會生成一個單獨的動畵幀。它生成了一個包含兩種顔色的201&201大小的圖片,白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette里的第0個值),這里我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色,生成一張新圖片。其結果會append到之前結果之後。這里我們用到了append(參考4.2.1)這個內置函數,將結果appen到anim中的幀列表末尾,併會設置一個默認的80ms的延遲值。最終循環結束,所有的延遲值也被編碼進了GIF圖片中,併將結果寫入到輸齣流。out這個變量是io.Writer類型,這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標,很快我們就可以看到了。</p>
|
||||
<p>當我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩個單詞),通常我們隻需要用最後那個單詞表示這個包就可以。所以當我們寫color.White時,這個變量指向的是image/color包里的變量,同理gif.GIF是屬於image/gif包里的變量。</p>
|
||||
<p>這個程序里的常量聲明給齣了一繫列的常量值,常量是指在程序編譯後運行時始終都不會變化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會齣現在包級别,所以這些常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數體內部,那麽這種常量就隻能在函數體內用。目前常量聲明的值必鬚是一個數字值、字符串或者一個固定的boolean值。</p>
|
||||
<p>[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們説的複合聲明(4.2和4.4.1節有説明)。這是實例化Go語言里的複合類型的一種寫法。這里的前者生成的是一個slice切片,後者生成的是一個struct結構體。</p>
|
||||
<p>gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合,不同的類型集合在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。這種寫法會生成一個struct變量,併且其內部變量LoopCount字段會被設置爲nframes;而其它的字段會被設置爲各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問,就像在最後兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。</p>
|
||||
<p>lissajous函數內部有兩層嵌套的for循環。外層循環會循環64次,每一次都會生成一個單獨的動畵幀。它生成了一個包含兩種顔色的201&201大小的圖片,白色和黑色。所有像素點都會被默認設置爲其零值(也就是palette里的第0個值),這里我們設置的是白色。每次經過內存循環都會通過設置像素爲黑色,生成一張新圖片。其結果會append到之前結果之後。這里我們用到了append(參考4.2.1)這個內置函數,將結果appen到anim中的幀列表末尾,併會設置一個默認的80ms的延遲值。最終循環結束,所有的延遲值也被編碼進了GIF圖片中,併將結果寫入到輸齣流。out這個變量是io.Writer類型,這個類型讓我們可以可以讓我們把輸齣結果寫到很多目標,很快我們就可以看到了。</p>
|
||||
<p>內存循環設置了兩個偏振。x軸偏振使用的是一個sin函數。y軸偏振也是一個正絃波,但是其其相對x軸的偏振是一個0-3的隨機值,併且初始偏振值是一個零值,併隨着動畵的每一幀逐漸增加。循環會一直跑到x軸完成五次完整的循環。每一步它都會調用SetColorIndex來爲(x, y)點來染黑色。</p>
|
||||
<p>main函數調用了lissajous函數,併且用它來向標準輸齣中打印信息,所以下面這個命令會像圖1.1中産生一個GIF動畵。</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/lissajous
|
||||
<pre><code>$ go build gopl.io/ch1/lissajous
|
||||
$ ./lissajous >out.gif
|
||||
</code></pre>
|
||||
<pre><code>Exercise 1.5: 脩改前面的Lissajous程序里的調色闆,由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值,三個十六進製的字符串分别代表紅、緑、藍像素。
|
||||
Exercise 1.6: 脩改Lissajous程序,脩改其調色闆來生成更豐富的顔色,然後脩改SetColorIndex的第三個參數,看看顯示結果吧。
|
||||
</code></pre>
|
||||
</code></pre><p><strong>練習 1.5:</strong> 脩改前面的Lissajous程序里的調色闆,由緑色改爲黑色。我們可以用color.RGBA{0xRR, 0xGG, 0xBB}來得到#RRGGBB這個色值,三個十六進製的字符串分别代表紅、緑、藍像素。</p>
|
||||
<p><strong>練習 1.6:</strong> 脩改Lissajous程序,脩改其調色闆來生成更豐富的顔色,然後脩改SetColorIndex的第三個參數,看看顯示結果吧。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.5" data-chapter-title="穫取URL" data-filepath="ch1/ch1-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.5" data-chapter-title="穫取URL" data-filepath="ch1/ch1-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2023,12 +2023,13 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="15-穫取url">1.5 穫取URL</h2>
|
||||
<p>對於很多應用來説,訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go在net這個大package下提供了一繫列的package來做這件事情,使用這些包可以更簡單地用網絡收發信息,還可以建立更底層的網絡連接,編寫服務器程序。在這些情景下,Go原生的併發特性(在第八章中會介紹)就顯得尤其好用了。</p>
|
||||
<p>爲了最簡單地展示基於HTTP穫取信息的方式,下面給齣一個示例程序fetch,這個程序將穫取對應的url,併將其源文本打印齣來;這個例子的靈感來源於curl工具(譯註:unix下的一個工具)。當然了,curl提供的功能更爲複雜豐富,這里我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。</p>
|
||||
<h2 id="15-穫取url">1.5. 穫取URL</h2>
|
||||
<p>對於很多現代應用來説,訪問互聯網上的信息和訪問本地文件繫統一樣重要。Go語言在net這個強大package的幫助下提供了一繫列的package來做這件事情,使用這些包可以更簡單地用網絡收發信息,還可以建立更底層的網絡連接,編寫服務器程序。在這些情景下,Go語言原生的併發特性(在第八章中會介紹)就顯得尤其好用了。</p>
|
||||
<p>爲了最簡單地展示基於HTTP穫取信息的方式,下面給齣一個示例程序fetch,這個程序將穫取對應的url,併將其源文本打印齣來;這個例子的靈感來源於curl工具(譯註:unix下的一個網絡相關的工具)。當然了,curl提供的功能更爲複雜豐富,這里我們隻編寫最簡單的樣例。之後我們還會在本書中經常用到這個例子。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/fetch
|
||||
<span class="hljs-comment">// Fetch prints the content found at a URL.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
|
||||
<span class="hljs-keyword">import</span> (
|
||||
<span class="hljs-string">"fmt"</span>
|
||||
<span class="hljs-string">"io/ioutil"</span>
|
||||
@@ -2053,23 +2054,24 @@
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這個程序從兩個package中導入了函數,net/http和io/ioutil,http.Get函數是創建HTTP請求的函數,如果穫取過程沒有齣錯,那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫齣到標準輸齣流中。</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/fetch
|
||||
<p>這個程序從兩個package中導入了函數,net/http和io/ioutil包,http.Get函數是創建HTTP請求的函數,如果穫取過程沒有齣錯,那麽會在resp這個結構體中得到訪問的請求結果。resp的Body字段包括一個可讀的服務器響應流。這之後ioutil.ReadAll函數從response中讀取到全部內容;其結果保存在變量b中。resp.Body.Close這一句會關閉resp的Body流,防止資源洩露,Printf函數會將結果b寫齣到標準輸齣流中。</p>
|
||||
<pre><code>$ go build gopl.io/ch1/fetch
|
||||
$ ./fetch http://gopl.io
|
||||
<html>
|
||||
<head>
|
||||
<title>The Go Programming Language</title>title>
|
||||
...
|
||||
</code></pre>
|
||||
<p>HTTP請求如果失敗了的話,會得到下面這樣的結果:</p>
|
||||
<pre><code class="lang-bash">$ ./fetch http://bad.gopl.io
|
||||
</code></pre><p>HTTP請求如果失敗了的話,會得到下面這樣的結果:</p>
|
||||
<pre><code>$ ./fetch http://bad.gopl.io
|
||||
fetch: Get http://bad.gopl.io: dial tcp: lookup bad.gopl.io: no such host
|
||||
</code></pre>
|
||||
<p>無論哪種失敗原因,我們的程序都用了os.Exit函數來終止進程,併且返迴一個status錯誤碼,其值爲1。</p>
|
||||
<pre><code>Exercise1.7: 函數調用io.Copy(dst, src)會從src中讀取內容,併將讀到的結果寫入到dst中,使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout,避免申請一個緩衝區(例子中的b)來存儲。記得處理io.Copy返迴結果中的錯誤。
|
||||
Exercise 1.8: 脩改fetch這個范例,如果輸入的url參數沒有http://前綴的話,爲這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。
|
||||
Exercise 1.9: 脩改fetch打印齣HTTP協議的狀態碼,可以從resp.Status變量得到該狀態碼。
|
||||
</code></pre>
|
||||
</code></pre><p>譯註:在大兲朝的網絡環境下很容易重現這種錯誤,下面是Windows下運行得到的錯誤信息:</p>
|
||||
<pre><code>$ go run main.go http://gopl.io
|
||||
fetch: Get http://gopl.io: dial tcp: lookup gopl.io: getaddrinfow: No such host is known.
|
||||
</code></pre><p>無論哪種失敗原因,我們的程序都用了os.Exit函數來終止進程,併且返迴一個status錯誤碼,其值爲1。</p>
|
||||
<p><strong>練習 1.7:</strong> 函數調用io.Copy(dst, src)會從src中讀取內容,併將讀到的結果寫入到dst中,使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout,避免申請一個緩衝區(例子中的b)來存儲。記得處理io.Copy返迴結果中的錯誤。</p>
|
||||
<p><strong>練習 1.8:</strong> 脩改fetch這個范例,如果輸入的url參數沒有 <code>http://</code> 前綴的話,爲這個url加上該前綴。你可能會用到strings.HasPrefix這個函數。</p>
|
||||
<p><strong>練習 1.9:</strong> 脩改fetch打印齣HTTP協議的狀態碼,可以從resp.Status變量得到該狀態碼。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.6" data-chapter-title="併發穫取多個URL" data-filepath="ch1/ch1-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.6" data-chapter-title="併發穫取多個URL" data-filepath="ch1/ch1-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2023,8 +2023,8 @@
|
||||
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="16-併發穫取多個url">1.6 併發穫取多個URL</h2>
|
||||
<p>Go語言最有意思併且最新奇的特性就是其對併發編程的支持了。併發編程是一個大話題,在第八章和第九章中會講到。這里我們隻淺嚐輒止地來體驗一下Go語言里的goroutine和channel。</p>
|
||||
<h2 id="16-併發穫取多個url">1.6. 併發穫取多個URL</h2>
|
||||
<p>Go語言最有意思併且最新奇的特性就是其對併發編程的支持了。併發編程是一個大話題,在第八章和第九章中會專門講到。這里我們隻淺嚐輒止地來體驗一下Go語言里的goroutine和channel。</p>
|
||||
<p>下面的例子fetchall,和上面的fetch程序所要做的工作是一致的,但是這個fetchall的特别之處在於它會同時去穫取所有的URL,所以這個程序的穫取時間不會超過執行時間最長的那一個任務,而不會像前面的fetch程序一樣,執行時間是所有任務執行時間之和。這次的fetchall程序隻會打印穫取的內容大小和經過的時間,不會像上面那樣打印齣穫取的內容。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/fetchall
|
||||
<span class="hljs-comment">// Fetchall fetches URLs in parallel and reports their times and sizes.</span>
|
||||
@@ -2069,17 +2069,16 @@
|
||||
}
|
||||
</code></pre>
|
||||
<p>下面是一個使用的例子</p>
|
||||
<pre><code class="lang-bash">$ go build gopl.io/ch1/fetchall
|
||||
<pre><code>$ go build gopl.io/ch1/fetchall
|
||||
$ ./fetchall https://golang.org http://gopl.io https://godoc.org
|
||||
<span class="hljs-number">0.14</span>s <span class="hljs-number">6852</span> https://godoc.org
|
||||
<span class="hljs-number">0.16</span>s <span class="hljs-number">7261</span> https://golang.org
|
||||
<span class="hljs-number">0.48</span>s <span class="hljs-number">2475</span> http://gopl.io
|
||||
<span class="hljs-number">0.48</span>s elapsed
|
||||
</code></pre>
|
||||
<p>goroutine是一種函數的併行執行方式,而channel是用來在goroutine之間進行參數傳遞。main函數卽運行在一個goroutine中,而go function則表示創建一個新的goroutine,併讓這個函數去這個新的goroutine里執行。</p>
|
||||
<p>main函數中用make函數創建了一個傳遞string類型參數的channel,對每一個命令行參數,我們都用go這個關鍵字來創建一個goroutine,併且讓函數在這個goroutine異步執行http.Get方法。這個程序里的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中,因爲我們需要這個方法返迴的字節數,但是又不想要其內容。每當請求返迴內容時,fetch函數都會往ch這個channel里寫入一個字符串,由main函數里的第二個for循環來處理併打印channel里的這個字符串。</p>
|
||||
0.14s 6852 https://godoc.org
|
||||
0.16s 7261 https://golang.org
|
||||
0.48s 2475 http://gopl.io
|
||||
0.48s elapsed
|
||||
</code></pre><p>goroutine是一種函數的併發執行方式,而channel是用來在goroutine之間進行參數傳遞。main函數也是運行在一個goroutine中,而go function則表示創建一個新的goroutine,併在這個這個新的goroutine里執行這個函數。</p>
|
||||
<p>main函數中用make函數創建了一個傳遞string類型參數的channel,對每一個命令行參數,我們都用go這個關鍵字來創建一個goroutine,併且讓函數在這個goroutine異步執行http.Get方法。這個程序里的io.Copy會把響應的Body內容拷貝到ioutil.Discard輸齣流中(譯註:這是一個垃圾桶,可以向里面寫一些不需要的數據),因爲我們需要這個方法返迴的字節數,但是又不想要其內容。每當請求返迴內容時,fetch函數都會往ch這個channel里寫入一個字符串,由main函數里的第二個for循環來處理併打印channel里的這個字符串。</p>
|
||||
<p>當一個goroutine嚐試在一個channel上做send或者receive操作時,這個goroutine會阻塞在調用處,直到另一個goroutine往這個channel里寫入、或者接收了值,這樣兩個goroutine纔會繼續執行操作channel完成之後的邏輯。在這個例子中,每一個fetch函數在執行時都會往channel里發送一個值(ch <- expression),主函數接收這些值(<-ch)。這個程序中我們用main函數來所有fetch函數傳迴的字符串,可以避免在goroutine異步執行時同時結束。</p>
|
||||
<p>Exercise 1.10: 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差别,併且每次穫取到的響應內容是否一致,脩改本節中的程序,將響應結果輸齣,以便於進行對比。</p>
|
||||
<p><strong>練習 1.10:</strong> 找一個數據量比較大的網站,用本小節中的程序調研網站的緩存策略,對每個URL執行兩遍請求,査看兩次時間是否有較大的差别,併且每次穫取到的響應內容是否一致,脩改本節中的程序,將響應結果輸齣,以便於進行對比。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.7" data-chapter-title="Web服務" data-filepath="ch1/ch1-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.7" data-chapter-title="Web服務" data-filepath="ch1/ch1-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="17-web服務">1.7. Web服務</h2>
|
||||
<p>Go的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中,我們會展示一個微型服務器,這個服務的功能是返迴當前用戶正在訪問的URL。也就是説比如用戶訪問的是<a href="http://localhost:8000/hello,那麽響應是URL.Path" target="_blank">http://localhost:8000/hello,那麽響應是URL.Path</a> = "hello"。</p>
|
||||
<p>Go語言的內置庫讓我們寫一個像fetch這樣例子的web服務器變得異常地簡單。在本節中,我們會展示一個微型服務器,這個服務的功能是返迴當前用戶正在訪問的URL。也就是説比如用戶訪問的是 <a href="http://localhost:8000/hello" target="_blank">http://localhost:8000/hello</a> ,那麽響應是URL.Path = "hello"。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server1
|
||||
<span class="hljs-comment">// Server1 is a minimal "echo" server.</span>
|
||||
<span class="hljs-keyword">package</span> main
|
||||
@@ -2042,10 +2042,10 @@
|
||||
|
||||
<span class="hljs-comment">// handler echoes the Path component of the request URL r.</span>
|
||||
<span class="hljs-keyword">func</span> handler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, <span class="hljs-string">"URL.Path = %q\n"</span>, r.URL.Path)
|
||||
fmt.Fprintf(w, <span class="hljs-string">"URL.Path = %q\n"</span>, r.URL.Path)
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們隻用了八九行就實現了這個程序,這都是多虧了標準庫里的方法已經幫我們處理了大多數的工作。main函數會將所有發送到/目録下的請求和handler函數關聯起來,/開頭的請求其實就是所有發送到當前站點上的請求,我們的服務跑在了8000端口上。發送到這個服務的“請求”是一個http.Request類型的對象,這個對象中包含了請求中的一繫列相關字段,其中就包括我們需要的URL。當請求到達服務器時,這個請求會被傳給handler函數來處理,這個函數會將/hello這個路徑從請求的URL中解析齣來,然後把其發送到響應中,這里我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。</p>
|
||||
<p>我們隻用了八九行代碼就實現了一個個Web服務程序,這都是多虧了標準庫里的方法已經幫我們處理了大量的工作。main函數會將所有發送到/路徑下的請求和handler函數關聯起來,/開頭的請求其實就是所有發送到當前站點上的請求,我們的服務跑在了8000端口上。發送到這個服務的“請求”是一個http.Request類型的對象,這個對象中包含了請求中的一繫列相關字段,其中就包括我們需要的URL。當請求到達服務器時,這個請求會被傳給handler函數來處理,這個函數會將/hello這個路徑從請求的URL中解析齣來,然後把其發送到響應中,這里我們用的是標準輸齣流的fmt.Fprintf。Web服務會在第7.7節中詳細闡述。</p>
|
||||
<p>讓我們在後颱運行這個服務程序。如果你的操作繫統是Mac OS X或者Linux,那麽在運行命令的末尾加上一個&符號,卽可讓程序簡單地跑在後颱,而在windows下,你需要在另外一個命令行窗口去運行這個程序了。</p>
|
||||
<pre><code>$ go run src/gopl.io/ch1/server1/main.go &
|
||||
</code></pre><p>現在我們可以通過命令行來發送客戶端請求了:</p>
|
||||
@@ -2054,8 +2054,8 @@ $ ./fetch http://localhost:8000
|
||||
URL.Path = "/"
|
||||
$ ./fetch http://localhost:8000/help
|
||||
URL.Path = "/help"
|
||||
</code></pre><p>另外我們還可以直接在瀏覽器里訪問這個URL,然後得到返迴結果,如圖1.2:
|
||||
<img src="../images/ch1-02.png" alt=""></p>
|
||||
</code></pre><p>另外我們還可以直接在瀏覽器里訪問這個URL,然後得到返迴結果,如圖1.2:</p>
|
||||
<p><img src="../images/ch1-02.png" alt=""></p>
|
||||
<p>在這個服務的基礎上疊加特性是很容易的。一種比較實用的脩改是爲訪問的url添加某種狀態。比如,下面這個版本輸齣了同樣的內容,但是會對請求的次數進行計算;對URL的請求結果會包含各種URL被訪問的總次數,直接對/count這個URL的訪問要除外。</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server2
|
||||
<span class="hljs-comment">// Server2 is a minimal "echo" and counter server.</span>
|
||||
@@ -2092,8 +2092,8 @@ URL.Path = "/help"
|
||||
mu.Unlock()
|
||||
}
|
||||
</code></pre>
|
||||
<p>這個服務器有兩個請求處理函數,請求的url會決定具體調用哪一個:對/count這個url的請求會調用到count這個函數,其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾,那麽所有以該url爲前綴的url都會被這條規則匹配。在這些代碼的背後,服務器每一次接收請求處理時都會另起一個goroutine,這樣服務器就可以同一時間處理多數請求。然而在併發情況下,假如眞的有兩個請求同一時刻去更新count,那麽這個值可能併不會被正確地增加;這個程序可能會被引發一個嚴重的bug:競態條件(參見9.1)。爲了避免這個問題,我們必鬚保證每次脩改變量的最多隻能有一個goroutine,這也就是代碼里的mu.Lock()和mu.Unlock()調用將脩改count的所有行爲包在中間的目的。第九章中我們會進一步講解共享變量。</p>
|
||||
<p>下面是一個更爲豐富的例子,handler函數會把請求的http頭和請求的form數據都打印齣來,這樣可以讓檢査和調試這個服務更爲方便</p>
|
||||
<p>這個服務器有兩個請求處理函數,請求的url會決定具體調用哪一個:對/count這個url的請求會調用到count這個函數,其它所有的url都會調用默認的處理函數。如果你的請求pattern是以/結尾,那麽所有以該url爲前綴的url都會被這條規則匹配。在這些代碼的背後,服務器每一次接收請求處理時都會另起一個goroutine,這樣服務器就可以同一時間處理多數請求。然而在併發情況下,假如眞的有兩個請求同一時刻去更新count,那麽這個值可能併不會被正確地增加;這個程序可能會被引發一個嚴重的bug:競態條件(參見9.1)。爲了避免這個問題,我們必鬚保證每次脩改變量的最多隻能有一個goroutine,這也就是代碼里的mu.Lock()和mu.Unlock()調用將脩改count的所有行爲包在中間的目的。第九章中我們會進一步講解共享變量。</p>
|
||||
<p>下面是一個更爲豐富的例子,handler函數會把請求的http頭和請求的form數據都打印齣來,這樣可以讓檢査和調試這個服務更爲方便:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch1/server3
|
||||
<span class="hljs-comment">// handler echoes the HTTP request.</span>
|
||||
<span class="hljs-keyword">func</span> handler(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -2107,7 +2107,7 @@ URL.Path = "/help"
|
||||
log.Print(err)
|
||||
}
|
||||
<span class="hljs-keyword">for</span> k, v := <span class="hljs-keyword">range</span> r.Form {
|
||||
fmt.Fprintf(w, <span class="hljs-string">"Form[%q] = %q\n"</span>, k, v)
|
||||
fmt.Fprintf(w, <span class="hljs-string">"Form[%q] = %q\n"</span>, k, v)
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
@@ -2118,7 +2118,7 @@ Header["Connection"] = ["keep-alive"]
|
||||
Header["Accept"] = ["text/html,application/xhtml+xml,application/xml;..."] Header["User-Agent"] = ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)..."] Host = "localhost:8000"
|
||||
RemoteAddr = "127.0.0.1:59911"
|
||||
Form["q"] = ["query"]
|
||||
</code></pre><p>可以看到這里的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作爲循環的變量聲明齣現在if語句的最前面,這一點對錯誤處理很有用處。我們還可以像下面這樣寫(當然看起來就長了一些):</p>
|
||||
</code></pre><p>可以看到這里的ParseForm被嵌套在了if語句中。Go語言允許這樣的一個簡單的語句結果作爲循環的變量聲明齣現在if語句的最前面,這一點對錯誤處理很有用處。我們還可以像下面這樣寫(當然看起來就長了一些):</p>
|
||||
<pre><code class="lang-go">err := r.ParseForm()
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
log.Print(err)
|
||||
@@ -2127,19 +2127,21 @@ Form["q"] = ["query"]
|
||||
<p>用if和ParseForm結合可以讓代碼更加簡單,併且可以限製err這個變量的作用域,這麽做是很不錯的。我們會在2.7節中講解作用域。</p>
|
||||
<p>在這些程序中,我們看到了很多不同的類型被輸齣到標準輸齣流中。比如前面的fetch程序,就把HTTP的響應數據拷貝到了os.Stdout,或者在lissajous程序里我們輸齣的是一個文件。fetchall程序則完全忽略到了HTTP的響應體,隻是計算了一下響應體的大小,這個程序中把響應體拷貝到了ioutil.Discard。在本節的web服務器程序中則是用fmt.Fprintf直接寫到了http.ResponseWriter中。</p>
|
||||
<p>盡管這三種具體的實現流程併不太一樣,他們都實現一個共同的接口,卽當它們被調用需要一個標準流輸齣時都可以滿足。這個接口叫作io.Writer,在7.1節中會詳細討論。</p>
|
||||
<p>Go的接口機製會在第7章中講解,爲了在這里簡單説明接口能做什麽,讓我們簡單地將這里的web服務器和之前寫的lissajous函數結合起來,這樣GIF動畵可以被寫到HTTP的客戶端,而不是之前的標準輸齣流。隻要在web服務器的代碼里加入下面這幾行。</p>
|
||||
<pre><code>handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
<p>Go語言的接口機製會在第7章中講解,爲了在這里簡單説明接口能做什麽,讓我們簡單地將這里的web服務器和之前寫的lissajous函數結合起來,這樣GIF動畵可以被寫到HTTP的客戶端,而不是之前的標準輸齣流。隻要在web服務器的代碼里加入下面這幾行。</p>
|
||||
<pre><code class="lang-Go">handler := <span class="hljs-keyword">func</span>(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
}
|
||||
http.HandleFunc("/", handler)
|
||||
</code></pre><p>或者另一種等價形式:</p>
|
||||
<pre><code>http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.HandleFunc(<span class="hljs-string">"/"</span>, handler)
|
||||
</code></pre>
|
||||
<p>或者另一種等價形式:</p>
|
||||
<pre><code class="lang-Go">http.HandleFunc(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">func</span>(w http.ResponseWriter, r *http.Request) {
|
||||
lissajous(w)
|
||||
})
|
||||
</code></pre><p>HandleFunc函數的第二個參數是一個函數的字面值,也就是一個在使用時定義的匿名函數。這些內容我們會在5.6節中講解。</p>
|
||||
<p>做完這些脩改之後,在瀏覽器里訪問<a href="http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵。" target="_blank">http://localhost:8000。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵。</a></p>
|
||||
<pre><code>Exercise 1.12:脩改Lissajour服務,從URL讀取變量,比如你可以訪問http://localhost:8000/?cycles=20這個URL,這樣訪問可以將程序里的cycles默認的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函數。你可以在dodoc里査看strconv.Atoi的詳細説明。
|
||||
</code></pre><p><img src="../images/ch1-03.png" alt=""></p>
|
||||
</code></pre>
|
||||
<p>HandleFunc函數的第二個參數是一個函數的字面值,也就是一個在使用時定義的匿名函數。這些內容我們會在5.6節中講解。</p>
|
||||
<p>做完這些脩改之後,在瀏覽器里訪問 <a href="http://localhost:8000" target="_blank">http://localhost:8000</a> 。每次你載入這個頁面都可以看到一個像圖1.3那樣的動畵。</p>
|
||||
<p><strong>練習 1.12:</strong> 脩改Lissajour服務,從URL讀取變量,比如你可以訪問 <a href="http://localhost:8000/?cycles=20" target="_blank">http://localhost:8000/?cycles=20</a> 這個URL,這樣訪問可以將程序里的cycles默認的5脩改爲20。字符串轉換爲數字可以調用strconv.Atoi函數。你可以在dodoc里査看strconv.Atoi的詳細説明。</p>
|
||||
<p><img src="../images/ch1-03.png" alt=""></p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1.8" data-chapter-title="本章要點" data-filepath="ch1/ch1-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1.8" data-chapter-title="本章要點" data-filepath="ch1/ch1-08.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,52 +2024,52 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="18-本章要點">1.8. 本章要點</h2>
|
||||
<p>本章中對Go語言做了一些介紹,實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這里我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以簡單有個印象。</p>
|
||||
<p>控製流:在本章我們隻介紹了if控製和for,但是沒有提到switch多路選擇。這里是一個簡單的switch的例子:</p>
|
||||
<p>本章中對Go語言做了一些介紹,實際上Go語言還有很多方面在這有限的篇幅中還沒有覆蓋到。這里我們會把沒有講到的內容也做一些簡單的介紹,這樣讀者在之後看到完整的內容之前,也可以有個簡單印象。</p>
|
||||
<p><strong>控製流:</strong> 在本章我們隻介紹了if控製和for,但是沒有提到switch多路選擇。這里是一個簡單的switch的例子:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">switch</span> coinflip() {
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-string">"heads"</span>:
|
||||
heads++
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-string">"tails"</span>:
|
||||
tails++
|
||||
<span class="hljs-keyword">default</span>:
|
||||
fmt.Println(<span class="hljs-string">"landed on edge!"</span>)
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-string">"heads"</span>:
|
||||
heads++
|
||||
<span class="hljs-keyword">case</span> <span class="hljs-string">"tails"</span>:
|
||||
tails++
|
||||
<span class="hljs-keyword">default</span>:
|
||||
fmt.Println(<span class="hljs-string">"landed on edge!"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>在翻轉硬幣的時候,例子里的coinflip函數返迴幾種不同的結果,每一個case都會對應個返迴結果,這里需要註意,Go語言併不需要顯式地去在每一個case後寫break,語言默認執行完case後的邏輯語句會自動退齣。當然了,如果你想要相鄰的幾個case都執行同一邏輯的話,需要自己顯式地寫上一個fallthrough語句來覆蓋這種默認行爲。不過fallthrough語句在一般的編程中用到得很少。</p>
|
||||
<p>go里的switch還可以不帶操作對象;可以直接羅列多種條件,像其它語言里面的多個if else一樣,下面是一個例子:</p>
|
||||
<p>Go語言里的switch還可以不帶操作對象(譯註:switch不帶操作對象時默認用true值代替,然後將每個case的表達式和true值進行比較);可以直接羅列多種條件,像其它語言里面的多個if else一樣,下面是一個例子:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">func</span> Signum(x <span class="hljs-typename">int</span>) <span class="hljs-typename">int</span> {
|
||||
<span class="hljs-keyword">switch</span> {
|
||||
<span class="hljs-keyword">case</span> x > <span class="hljs-number">0</span>:
|
||||
<span class="hljs-keyword">return</span> +<span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">default</span>:
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">case</span> x < <span class="hljs-number">0</span>:
|
||||
<span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">case</span> x > <span class="hljs-number">0</span>:
|
||||
<span class="hljs-keyword">return</span> +<span class="hljs-number">1</span>
|
||||
<span class="hljs-keyword">default</span>:
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">case</span> x < <span class="hljs-number">0</span>:
|
||||
<span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>這種形式叫做無tag switch(tagless switch);這和switch true是等價的。</p>
|
||||
<p>像for和if控製語句一樣,switch也可以緊跟一個簡短的變量聲明,一個自增表達式、賦值語句,或者一個函數調用。</p>
|
||||
<p>break和continue語句會改變控製流。和其它語言中的break和continue一樣,break會中斷當前的循環,併開始執行循環之後的內容,而continue會中跳過當前循環,併開始執行下一次循環。這兩個語句除了可以控製for循環,還可以用來控製switch和select語句(之後會講到),在1.3節中我們看到,continue會跳過是內層的循環,如果我們想跳過的是更外層的循環的話,我們可以在相應的位置加上label,這樣break和continue就可以根據我們的想法來continue和break任意循環。這看起來甚至有點像goto語句的作用了。當然,一般程序員也不會用到這種操作。這兩種行爲更多地被用到機器生成的代碼中。</p>
|
||||
<p>命名類型:類型聲明使得我們可以很方便地給一個特殊類型一個名字。因爲struct類型聲明通常非常地長,所以我們總要給這種struct取一個名字。本章中就有這樣一個例子,2d點類型:</p>
|
||||
<p><strong>命名類型:</strong> 類型聲明使得我們可以很方便地給一個特殊類型一個名字。因爲struct類型聲明通常非常地長,所以我們總要給這種struct取一個名字。本章中就有這樣一個例子,二維點類型:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Point <span class="hljs-keyword">struct</span> {
|
||||
X, Y <span class="hljs-typename">int</span>
|
||||
}
|
||||
<span class="hljs-keyword">var</span> p Point
|
||||
</code></pre>
|
||||
<p>類型聲明和命名類型會在第二章中介紹。</p>
|
||||
<p>指針:Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據結構。在其它語言中,比如C語言,指針是完全不受約束的。在另外一些語言中,指針一般被稱爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。go在這兩種范圍中取得了一個平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以穫取指針指向的變量內容,但是在go語言里沒有指針運算,也就是不像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。</p>
|
||||
<p>方法和接口:方法是和命名類型關聯的一類函數。Go語言里比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細説明這些內容。</p>
|
||||
<p>包(packages):Go語言提供了一些很好用的package,併且這些package是可以擴展的。Go語言社區已經創造併且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書,我們會講解一些重要的標準庫內的package,但是還是有很多我們沒有篇幅去説明,因爲我們沒法在這樣的厚度的書里去做一部代碼大全。</p>
|
||||
<p>在你開始寫一個新程序之前,最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在<a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 和 <a href="https://godoc.org" target="_blank">https://godoc.org</a> 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行閲讀標準庫的文檔。比如下面這個例子。</p>
|
||||
<p><strong>指針:</strong> Go語言提供了指針。指針是一種直接存儲了變量的內存地址的數據類型。在其它語言中,比如C語言,指針操作是完全不受約束的。在另外一些語言中,指針一般被處理爲“引用”,除了到處傳遞這些指針之外,併不能對這些指針做太多事情。Go語言在這兩種范圍中取了一種平衡。指針是可見的內存地址,&操作符可以返迴一個變量的內存地址,併且*操作符可以穫取指針指向的變量內容,但是在Go語言里沒有指針運算,也就是不能像c語言里可以對指針進行加或減操作。我們會在2.3.2中進行詳細介紹。</p>
|
||||
<p><strong>方法和接口:</strong> 方法是和命名類型關聯的一類函數。Go語言里比較特殊的是方法可以被關聯到任意一種命名類型。在第六章我們會詳細地講方法。接口是一種抽象類型,這種類型可以讓我們以同樣的方式來處理不同的固有類型,不用關心它們的具體實現,而隻需要關註它們提供的方法。第七章中會詳細説明這些內容。</p>
|
||||
<p><strong>包(packages):</strong> Go語言提供了一些很好用的package,併且這些package是可以擴展的。Go語言社區已經創造併且分享了很多很多。所以Go語言編程大多數情況下就是用已有的package來寫我們自己的代碼。通過這本書,我們會講解一些重要的標準庫內的package,但是還是有很多我們沒有篇幅去説明,因爲我們沒法在這樣的厚度的書里去做一部代碼大全。</p>
|
||||
<p>在你開始寫一個新程序之前,最好先去檢査一下是不是已經有了現成的庫可以幫助你更高效地完成這件事情。你可以在 <a href="https://golang.org/pkg" target="_blank">https://golang.org/pkg</a> 和 <a href="https://godoc.org" target="_blank">https://godoc.org</a> 中找到標準庫和社區寫的package。godoc這個工具可以讓你直接在本地命令行閲讀標準庫的文檔。比如下面這個例子。</p>
|
||||
<pre><code>$ go doc http.ListenAndServe
|
||||
package http // import "net/http"
|
||||
func ListenAndServe(addr string, handler Handler) error
|
||||
ListenAndServe listens on the TCP network address addr and then
|
||||
calls Serve with handler to handle requests on incoming connections.
|
||||
...
|
||||
</code></pre><p>註釋:我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個説明函數行爲的註釋也是一個好習慣。這些慣例很重要,因爲這些內容會被像godoc這樣的工具檢測到,併且在執行命令時顯示這些註釋。具體可以參考10.7.4。</p>
|
||||
<p>多行註釋可以用/<em> ... </em>/來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋。</p>
|
||||
</code></pre><p><strong>註釋:</strong> 我們之前已經提到過了在源文件的開頭寫的註釋是這個源文件的文檔。在每一個函數之前寫一個説明函數行爲的註釋也是一個好習慣。這些慣例很重要,因爲這些內容會被像godoc這樣的工具檢測到,併且在執行命令時顯示這些註釋。具體可以參考10.7.4。</p>
|
||||
<p>多行註釋可以用 <code>/* ... */</code> 來包裹,和其它大多數語言一樣。在文件一開頭的註釋一般都是這種形式,或者一大段的解釋性的註釋文字也會被這符號包住,來避免每一行都需要加//。在註釋中//和/*是沒什麽意義的,所以不要在註釋中再嵌入註釋。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
12
ch1/ch1.html
12
ch1/ch1.html
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="1" data-chapter-title="入門" data-filepath="ch1/ch1.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="1" data-chapter-title="入門" data-filepath="ch1/ch1.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,8 +2024,8 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第1章-入門">第1章 入門</h1>
|
||||
<p>本章會介紹Go語言里的一些基本組件。我們希望用信息和例子盡快帶你入門。本章和之後章節的例子都是針對眞實的開發案例給齣。本章我們隻是簡單地爲你介紹一些Go的入門例子,從簡單的文件處理、圖像處理到互聯網併發客戶端和服務端程序。當然,在第一章我們不會詳盡地一一去説明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。
|
||||
當你學習一門新語言時,你會有去用這門新語言去重寫自己以前熟悉語言例子的傾向。在學習Go的過程中,盡量避免這麽做。我們會向你演示如何纔能寫齣好的Go程序,所以請使用這里的代碼作爲你寫自己的Go程序時的指南。</p>
|
||||
<p>本章會介紹Go語言里的一些基本組件。我們希望用信息和例子盡快帶你入門。本章和之後章節的例子都是針對眞實的開發案例給齣。本章我們隻是簡單地爲你介紹一些Go語言的入門例子,從簡單的文件處理、圖像處理到互聯網併發客戶端和服務端程序。當然,在第一章我們不會詳盡地一一去説明細枝末節,不過用這些程序來學習一門新語言肯定是很有效的。</p>
|
||||
<p>當你學習一門新語言時,你會用這門新語言去重寫自己以前熟悉語言例子的傾向。在學習Go語言的過程中,盡量避免這麽做。我們會向你演示如何纔能寫齣好的Go語言程序,所以請使用這里的代碼作爲你寫自己的Go程序時的指南。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.1" data-chapter-title="簡介" data-filepath="ch10/ch10-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.1" data-chapter-title="簡介" data-filepath="ch10/ch10-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.2" data-chapter-title="導入路徑" data-filepath="ch10/ch10-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.2" data-chapter-title="導入路徑" data-filepath="ch10/ch10-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.3" data-chapter-title="包聲明" data-filepath="ch10/ch10-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.3" data-chapter-title="包聲明" data-filepath="ch10/ch10-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.4" data-chapter-title="導入聲明" data-filepath="ch10/ch10-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.4" data-chapter-title="導入聲明" data-filepath="ch10/ch10-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.5" data-chapter-title="匿名導入" data-filepath="ch10/ch10-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.5" data-chapter-title="匿名導入" data-filepath="ch10/ch10-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.6" data-chapter-title="包和命名" data-filepath="ch10/ch10-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.6" data-chapter-title="包和命名" data-filepath="ch10/ch10-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10.7" data-chapter-title="工具" data-filepath="ch10/ch10-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10.7" data-chapter-title="工具" data-filepath="ch10/ch10-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="10" data-chapter-title="包和工具" data-filepath="ch10/ch10.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="10" data-chapter-title="包和工具" data-filepath="ch10/ch10.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.1" data-chapter-title="go test" data-filepath="ch11/ch11-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.1" data-chapter-title="go test" data-filepath="ch11/ch11-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.2" data-chapter-title="測試函數" data-filepath="ch11/ch11-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.2" data-chapter-title="測試函數" data-filepath="ch11/ch11-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.3" data-chapter-title="測試覆蓋率" data-filepath="ch11/ch11-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.3" data-chapter-title="測試覆蓋率" data-filepath="ch11/ch11-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.4" data-chapter-title="基準測試" data-filepath="ch11/ch11-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.5" data-chapter-title="剖析" data-filepath="ch11/ch11-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.5" data-chapter-title="剖析" data-filepath="ch11/ch11-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11.6" data-chapter-title="示例函數" data-filepath="ch11/ch11-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11.6" data-chapter-title="示例函數" data-filepath="ch11/ch11-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="11" data-chapter-title="測試" data-filepath="ch11/ch11.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="11" data-chapter-title="測試" data-filepath="ch11/ch11.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.1" data-chapter-title="爲何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.1" data-chapter-title="爲何需要反射?" data-filepath="ch12/ch12-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.2" data-chapter-title="reflect.Type和reflect.Value" data-filepath="ch12/ch12-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.3" data-chapter-title="Display遞歸打印" data-filepath="ch12/ch12-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S表達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.4" data-chapter-title="示例: 編碼S表達式" data-filepath="ch12/ch12-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.5" data-chapter-title="通過reflect.Value脩改值" data-filepath="ch12/ch12-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S表達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.6" data-chapter-title="示例: 解碼S表達式" data-filepath="ch12/ch12-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.7" data-chapter-title="穫取結構體字段標識" data-filepath="ch12/ch12-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.8" data-chapter-title="顯示一個類型的方法集" data-filepath="ch12/ch12-08.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12.9" data-chapter-title="幾點忠告" data-filepath="ch12/ch12-09.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="12" data-chapter-title="反射" data-filepath="ch12/ch12.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.1" data-chapter-title="unsafe.Sizeof, Alignof 和 Offsetof" data-filepath="ch13/ch13-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.1" data-chapter-title="unsafe.Sizeof, Alignof 和 Offsetof" data-filepath="ch13/ch13-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,15 +2024,13 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="131-unsafesizeof-alignof-和-offsetof">13.1. unsafe.Sizeof, Alignof 和 Offsetof</h2>
|
||||
<p><code>unsafe.Sizeof</code> 函數返迴操作數在內存的字節大小, 可以是任意類型的表達式, 但是併不會對表達式進行求值. <code>Sizeof</code> 是一個 uintptr 類型的常量表達式, 因此返迴的結果可以用着數據的大小, 或者用作計算其他的常量.</p>
|
||||
<p>unsafe.Sizeof函數返迴操作數在內存中的字節大小,參數可以是任意類型的表達式,但是它併不會對表達式進行求值。一個Sizeof函數調用是一個對應uintptr類型的常量表達式,因此返迴的結果可以用作數組類型的長度大小,或者用作計算其他的常量。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"unsafe"</span>
|
||||
fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class="hljs-number">0</span>))) <span class="hljs-comment">// "8"</span>
|
||||
</code></pre>
|
||||
<p><code>Sizeof</code> 隻返迴數據結構中固定的部分, 例如字符串中指針和字符串長度部分, 但是併不包含字符串的內容. Go中非聚合類型通常有一個固定的尺寸, 盡管不同工具鏈的具體大小可能會有所不同. 考慮到可移植性, 引用類型或包含引用類型的大小在32位平颱上是4個字節, 在64位平颱上是8個字節.</p>
|
||||
<p>計算機加載和保存數據時, 如果內存地址合理地對齊的將會更有效率.
|
||||
例如 2 字節大小的 int16 類型應該是偶數, 一個4 字節大小的 rune 類型地址應該是 4 的倍數, 一個 8 字節大小的 float64, uint64 或 64-bit 指針 的地址應該是 8 字節對齊的. 但是對於再大的地址對齊倍數則是不需要的,
|
||||
卽使是 complex128 等較大的數據類型.</p>
|
||||
<p>由於這個因素,一個聚合類型(結構體或數組)的大小至少是所有字段或元素大小的總和, 或者更大因爲可能存在空洞. 空洞是編譯器自動添加的沒有被使用的空間, 用於保證後面每個字段或元素的地址相對於結構或數組的開始地址能夠合理地對齊.</p>
|
||||
<p>Sizeof函數返迴的大小隻包括數據結構中固定的部分,例如字符串對應結構體中的指針和字符串長度部分,但是併不包含指針指向的字符串的內容。Go語言中非聚合類型通常有一個固定的大小,盡管在不同工具鏈下生成的實際大小可能會有所不同。考慮到可移植性,引用類型或包含引用類型的大小在32位平颱上是4個字節,在64位平颱上是8個字節。</p>
|
||||
<p>計算機在加載和保存數據時,如果內存地址合理地對齊的將會更有效率。例如2字節大小的int16類型的變量地址應該是偶數,一個4字節大小的rune類型變量的地址應該是4的倍數,一個8字節大小的float64、uint64或64-bit指針類型變量的地址應該是8字節對齊的。但是對於再大的地址對齊倍數則是不需要的,卽使是complex128等較大的數據類型最多也隻是8字節對齊。</p>
|
||||
<p>由於地址對齊這個因素,一個聚合類型(結構體或數組)的大小至少是所有字段或元素大小的總和,或者更大因爲可能存在內存空洞。內存空洞是編譯器自動添加的沒有被使用的內存空間,用於保證後面每個字段或元素的地址相對於結構或數組的開始地址能夠合理地對齊(譯註:內存空洞可能會存在一些隨機數據,可能會對用unsafe包直接操作內存的處理産生影響)。</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -2043,11 +2041,11 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>bool</td>
|
||||
<td>1字節</td>
|
||||
<td>1個字節</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>intN, uintN, floatN, complexN</td>
|
||||
<td>N/8字節 (例如 float64 是 8字節)</td>
|
||||
<td>N/8個字節(例如float64是8個字節)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>int, uint, uintptr</td>
|
||||
@@ -2063,7 +2061,7 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
</tr>
|
||||
<tr>
|
||||
<td>[]T</td>
|
||||
<td>3個機器字(data,len, cap)</td>
|
||||
<td>3個機器字(data,len,cap)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>map</td>
|
||||
@@ -2083,13 +2081,13 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>Go的語言規范併沒有保證一個字段的聲明順序和內存中的順序是一致的, 所以理論上一個編譯器可以隨意地重新排列每個字段的內存布局, 隨着在寫作本書的時候編譯器還沒有這麽做. 下面的三個結構體有着相同的字段, 但是第一個比另外的兩個需要多 50% 的內存.</p>
|
||||
<pre><code class="lang-Go"> <span class="hljs-comment">// 64-bit 32-bit</span>
|
||||
<p>Go語言的規范併沒有要求一個字段的聲明順序和內存中的順序是一致的,所以理論上一個編譯器可以隨意地重新排列每個字段的內存位置,隨然在寫作本書的時候編譯器還沒有這麽做。下面的三個結構體雖然有着相同的字段,但是第一種寫法比另外的兩個需要多50%的內存。</p>
|
||||
<pre><code class="lang-Go"> <span class="hljs-comment">// 64-bit 32-bit</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">bool</span>; <span class="hljs-typename">float64</span>; <span class="hljs-typename">int16</span> } <span class="hljs-comment">// 3 words 4words</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">float64</span>; <span class="hljs-typename">int16</span>; <span class="hljs-typename">bool</span> } <span class="hljs-comment">// 2 words 3words</span>
|
||||
<span class="hljs-keyword">struct</span>{ <span class="hljs-typename">bool</span>; <span class="hljs-typename">int16</span>; <span class="hljs-typename">float64</span> } <span class="hljs-comment">// 2 words 3words</span>
|
||||
</code></pre>
|
||||
<p>雖然關於對齊算法的細節超齣了本書的范圍, 也不是每一個結構體都需要擔心這個問題, 不過有效的包裝可以使數據結構更加緊湊, 內存使用率和性能都可能受益.</p>
|
||||
<p>關於內存地址對齊算法的細節超齣了本書的范圍,也不是每一個結構體都需要擔心這個問題,不過有效的包裝可以使數據結構更加緊湊(譯註:未來的Go語言編譯器應該會默認優化結構體的順序,當然用於應該也能夠指定具體的內存布局,相同討論請參考 <a href="https://github.com/golang/go/issues/10014" target="_blank">Issue10014</a> ),內存使用率和性能都可能會受益。</p>
|
||||
<p><code>unsafe.Alignof</code> 函數返迴對應參數的類型需要對齊的倍數. 和 Sizeof 類似, Alignof 也是返迴一個常量表達式, 對應一個常量. 通常情況下布爾和數字類型需要對齊到它們本身的大小(最多8個字節), 其它的類型對齊到機器字大小.</p>
|
||||
<p><code>unsafe.Offsetof</code> 函數的參數必鬚是一個字段 <code>x.f</code>, 然後返迴 <code>f</code> 字段相對於 <code>x</code> 起始地址的偏移量, 包括可能的空洞.</p>
|
||||
<p>圖 13.1 顯示了一個結構體變量 x 以及其在32位和64位機器上的典型的內存. 灰色區域是空洞.</p>
|
||||
@@ -2099,21 +2097,19 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
c []<span class="hljs-typename">int</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>The table below shows the results of applying the three unsafe functions to x itself and to each of its three fields:</p>
|
||||
<p>下面顯示了應用三個函數對 x 和它的三個字段計算的結果:</p>
|
||||
<p>下面顯示了對x和它的三個字段調用unsafe包相關函數的計算結果:</p>
|
||||
<p><img src="../images/ch13-01.png" alt=""></p>
|
||||
<p>32位繫統:</p>
|
||||
<p>32位繫統:</p>
|
||||
<pre><code>Sizeof(x) = 16 Alignof(x) = 4
|
||||
Sizeof(x.a) = 1 Alignof(x.a) = 1 Offsetof(x.a) = 0
|
||||
Sizeof(x.b) = 2 Alignof(x.b) = 2 Offsetof(x.b) = 2
|
||||
Sizeof(x.c) = 12 Alignof(x.c) = 4 Offsetof(x.c) = 4
|
||||
</code></pre><p>64位繫統:</p>
|
||||
</code></pre><p>64位繫統:</p>
|
||||
<pre><code>Sizeof(x) = 32 Alignof(x) = 8
|
||||
Sizeof(x.a) = 1 Alignof(x.a) = 1 Offsetof(x.a) = 0
|
||||
Sizeof(x.b) = 2 Alignof(x.b) = 2 Offsetof(x.b) = 2
|
||||
Sizeof(x.c) = 24 Alignof(x.c) = 8 Offsetof(x.c) = 8
|
||||
</code></pre><p>雖然它們在不安全的 unsafe 包, 但是這幾個函數併不是眞的不安全,
|
||||
特别在需要優化內存空間時它們對於理解原生的內存布局很有幫助.</p>
|
||||
</code></pre><p>雖然這幾個函數在不安全的unsafe包,但是這幾個函數調用併不是眞的不安全,特别在需要優化內存空間時它們返迴的結果對於理解原生的內存布局很有幫助。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.2" data-chapter-title="unsafe.Pointer" data-filepath="ch13/ch13-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.2" data-chapter-title="unsafe.Pointer" data-filepath="ch13/ch13-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,19 +2024,17 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="132-unsafepointer">13.2. unsafe.Pointer</h2>
|
||||
<p>大多數指針類型寫成 <em>T, 含義是 "一個指向T類型變量的指針". <code>unsafe.Pointer</code> 是特别定義的一種指針類型, 它可以包含任意類型變量的地址. 當然, 我們不可以直接使用 </em>p 穫取 <code>unsafe.Pointer</code> 指針指向的眞實變量, 因爲我們併不知道變量的類型. 和普通指針一樣, <code>unsafe.Pointer</code> 指針是可以比較的, 支持和 nil 比較判斷是否爲空指針.</p>
|
||||
<p>一個普通的 <em>T 類型指針可以被轉化爲 <code>unsafe.Pointer</code> 類型指針, 併且一個 <code>unsafe.Pointer</code> 類型指針也可以被轉迴普通指針, 也可以是和 </em>T 不同類型的指針. 通過將 <code>*float64</code> 類型指針 轉化爲 <code>*uint64</code> 類型指針, 我們可以檢査一個浮點數變量的位模式.</p>
|
||||
<p>大多數指針類型會寫成<code>*T</code>,表示是“一個指向T類型變量的指針”。unsafe.Pointer是特别定義的一種指針類型(譯註:類似C語言中的<code>void*</code>類型的指針),它可以包含任意類型變量的地址。當然,我們不可以直接通過<code>*p</code>來穫取unsafe.Pointer指針指向的眞實變量的值,因爲我們併不知道變量的具體類型。和普通指針一樣,unsafe.Pointer指針也是可以比較的,併且支持和nil常量比較判斷是否爲空指針。</p>
|
||||
<p>一個普通的<code>*T</code>類型指針可以被轉化爲unsafe.Pointer類型指針,併且一個unsafe.Pointer類型指針也可以被轉迴普通的指針,被轉迴普通的指針類型併不需要和原始的<code>*T</code>類型相同。通過將<code>*float64</code>類型指針轉化爲<code>*uint64</code>類型指針,我們可以査看一個浮點數變量的位模式。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> math
|
||||
|
||||
<span class="hljs-keyword">func</span> Float64bits(f <span class="hljs-typename">float64</span>) <span class="hljs-typename">uint64</span> { <span class="hljs-keyword">return</span> *(*<span class="hljs-typename">uint64</span>)(unsafe.Pointer(&f)) }
|
||||
|
||||
fmt.Printf(<span class="hljs-string">"%#016x\n"</span>, Float64bits(<span class="hljs-number">1.0</span>)) <span class="hljs-comment">// "0x3ff0000000000000"</span>
|
||||
</code></pre>
|
||||
<p>通過新指針, 我們可以更新浮點數的位模式. 通過位模式操作浮點數是可以的, 但是更重要的意義是指針轉換讓我們可以在不破壞類型繫統的前提下向內存寫入任意的值.</p>
|
||||
<p>一個 <code>unsafe.Pointer</code> 指針也可以被轉化爲 uintptr 類似, 然後保存到指針型數值變量中, 用以做必要的指針運算.
|
||||
(第三章內容, uintptr是一個無符號的整型數, 足有保存一個地址.)
|
||||
這種轉換也是可逆的, 但是, 將 uintptr 轉爲 <code>unsafe.Pointer</code> 指針可能破壞類型繫統, 因爲併不是所有的數字都是有效的內存地址.</p>
|
||||
<p>許多將 <code>unsafe.Pointer</code> 指針 轉爲原生數字, 然後再轉爲 <code>unsafe.Pointer</code> 指針的操作是不安全的. 下面的例子需要將變量 x 的地址加上 b 字段的偏移轉化爲 *int16 類型指針, 然後通過該指針更新 <code>x.b</code>:</p>
|
||||
<p>通過轉爲新類型指針,我們可以更新浮點數的位模式。通過位模式操作浮點數是可以的,但是更重要的意義是指針轉換語法讓我們可以在不破壞類型繫統的前提下向內存寫入任意的值。</p>
|
||||
<p>一個unsafe.Pointer指針也可以被轉化爲uintptr類型,然後保存到指針型數值變量中(譯註:這隻是和當前指針相同的一個數字值,併不是一個指針),然後用以做必要的指針數值運算。(第三章內容,uintptr是一個無符號的整型數,足以保存一個地址)這種轉換雖然也是可逆的,但是將uintptr轉爲unsafe.Pointer指針可能會破壞類型繫統,因爲併不是所有的數字都是有效的內存地址。</p>
|
||||
<p>許多將unsafe.Pointer指針轉爲原生數字,然後再轉迴爲unsafe.Pointer類型指針的操作也是不安全的。比如下面的例子需要將變量x的地址加上b字段地址偏移量轉化爲<code>*int16</code>類型指針,然後通過該指針更新x.b:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">//gopl.io/ch13/unsafeptr</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> x <span class="hljs-keyword">struct</span> {
|
||||
@@ -2051,16 +2049,20 @@ pb := (*<span class="hljs-typename">int16</span>)(unsafe.Pointer(
|
||||
*pb = <span class="hljs-number">42</span>
|
||||
fmt.Println(x.b) <span class="hljs-comment">// "42"</span>
|
||||
</code></pre>
|
||||
<p>盡管寫法很繁瑣, 但在這里併不是一件壞事, 因爲這些功能應該很謹慎地使用. 不要試圖將引入可能而破壞代碼的正確性的 uintptr 臨時變量. 下面段代碼是不正確的:</p>
|
||||
<p>錯誤的原因很微妙. 有時候垃圾迴收器會移動一些變量以降低內存碎片的問題.這類垃圾迴收器被稱爲移動GC. 當一個變量被移動, 所有的保存改變量舊地址的指針必鬚同時被更新爲變量移動後的新地址. 從垃圾收集器的視角來看, 一個 <code>unsafe.Pointer</code> 是一個指針, 因此當變量被移動是對應的指針必鬚被更新, 但是 <code>uintptr</code> 隻是一個普通的數字, 所以其值不應該被改變. 上面錯誤的代碼因爲一個非指針的臨時變量 <code>tmp</code>, 導致垃圾收集器無法正確識别這個是一個指向變量 <code>x</code> 的指針. 第二個語句執行時, 變量 <code>x</code> 可能已經被轉移, 臨時變量 <code>tmp</code> 也就不在對應現在的 <code>&x.b</code>. 第三個賦值語句將徹底摧譭那個之前的那部分內存空間.</p>
|
||||
<p>有很多類似原因導致的錯誤. 例如這條語句:</p>
|
||||
<p>上面的寫法盡管很繁瑣,但在這里併不是一件壞事,因爲這些功能應該很謹慎地使用。不要試圖引入一個uintptr類型的臨時變量,因爲它可能會破壞代碼的安全性(譯註:這是眞正可以體會unsafe包爲何不安全的例子)。下面段代碼是錯誤的:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> subtly incorrect! </span>
|
||||
tmp := <span class="hljs-typename">uintptr</span>(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
|
||||
pb := (*<span class="hljs-typename">int16</span>)(unsafe.Pointer(tmp))
|
||||
*pb = <span class="hljs-number">42</span>
|
||||
</code></pre>
|
||||
<p>産生錯誤的原因很微妙。有時候垃圾迴收器會移動一些變量以降低內存碎片等問題。這類垃圾迴收器被稱爲移動GC。當一個變量被移動,所有的保存改變量舊地址的指針必鬚同時被更新爲變量移動後的新地址。從垃圾收集器的視角來看,一個unsafe.Pointer是一個指向變量的指針,因此當變量被移動是對應的指針也必鬚被更新;但是uintptr類型的臨時變量隻是一個普通的數字,所以其值不應該被改變。上面錯誤的代碼因爲引入一個非指針的臨時變量tmp,導致垃圾收集器無法正確識别這個是一個指向變量x的指針。當第二個語句執行時,變量x可能已經被轉移,這時候臨時變量tmp也就不再是現在的<code>&x.b</code>地址。第三個向之前無效地址空間的賦值語句將徹底摧譭整個程序!</p>
|
||||
<p>還有很多類似原因導致的錯誤。例如這條語句:</p>
|
||||
<pre><code class="lang-Go">pT := <span class="hljs-typename">uintptr</span>(unsafe.Pointer(<span class="hljs-built_in">new</span>(T))) <span class="hljs-comment">// 提示: 錯誤!</span>
|
||||
</code></pre>
|
||||
<p>這里併沒有指針引用 <code>new</code> 新創建的變量, 因此語句執行完成之後, 垃圾收集器有權迴收其內存空間, 所以返迴的 <code>pT</code> 保存將是無效的地址.</p>
|
||||
<p>目前的Go語言實現還沒有使用移動GC(未來可能實現), 但這不該是僥幸的理由: 當前的Go實現已經有移動變量的場景. 在5.2節我們提到goroutine的棧是根據需要動態增長的. 當這個時候, 原來棧中的所以變量可能需要被移動到新的更大的棧中, 所以我們無法確保變量的地址在整個使用週期內保持不變.</p>
|
||||
<p>在編寫本文時, 還沒有清晰的原則就指引Go程序員, 什麽樣 <code>unsafe.Pointer</code> 和 <code>uintptr</code> 的轉換是不安全的(參考 <a href="https://github.com/golang/go/issues/7192" target="_blank">Go issue7192</a>. 譯註: 該問題已經脩複.), 因此我們強烈建議按照最壞的方式處理. 將所有包含變量 <code>y</code> 地址的 <code>uintptr</code> 類型變量當作 BUG 處理, 同時減少不必要的 <code>unsafe.Pointer</code> 到 <code>uintptr</code> 的轉換. 在第一個例子中, 有三個到 <code>uintptr</code> 的轉換, 字段偏移量的運算, 所有的轉換全在一個表達式完成.</p>
|
||||
<p>當調用一個庫函數, 併且返迴的是 <code>uintptr</code> 類型是, 比如下面反射包中的相關函數,
|
||||
返迴的結果應該立卽轉換爲 <code>unsafe.Pointer</code> 以確保指針指向的是相同的變量.</p>
|
||||
<p>這里併沒有指針引用<code>new</code>新創建的變量,因此該語句執行完成之後,垃圾收集器有權馬上迴收其內存空間,所以返迴的pT將是無效的地址。</p>
|
||||
<p>雖然目前的Go語言實現還沒有使用移動GC(譯註:未來可能實現),但這不該是編寫錯誤代碼僥幸的理由:當前的Go語言實現已經有移動變量的場景。在5.2節我們提到goroutine的棧是根據需要動態增長的。當發送棧動態增長的時候,原來棧中的所以變量可能需要被移動到新的更大的棧中,所以我們併不能確保變量的地址在整個使用週期內是不變的。</p>
|
||||
<p>在編寫本文時,還沒有清晰的原則來指引Go程序員,什麽樣的unsafe.Pointer和uintptr的轉換是不安全的(參考 [Go issue7192](<a href="https://github.com/golang/go/issues/7192" target="_blank">https://github.com/golang/go/issues/7192</a> ). 譯註: 該問題已經關閉),因此我們強烈建議按照最壞的方式處理。將所有包含變量地址的uintptr類型變量當作BUG處理,同時減少不必要的unsafe.Pointer類型到uintptr類型的轉換。在第一個例子中,有三個轉換——字段偏移量到uintptr的轉換和轉迴unsafe.Pointer類型的操作——所有的轉換全在一個表達式完成。</p>
|
||||
<p>當調用一個庫函數,併且返迴的是uintptr類型地址時(譯註:普通方法實現的函數不盡量不要返迴該類型。下面例子是reflect包的函數,reflect包和unsafe包一樣都是采用特殊技術實現的,編譯器可能給它們開了後門),比如下面反射包中的相關函數,返迴的結果應該立卽轉換爲unsafe.Pointer以確保指針指向的是相同的變量。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> reflect
|
||||
|
||||
<span class="hljs-keyword">func</span> (Value) Pointer() <span class="hljs-typename">uintptr</span>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,23 +2024,21 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="133-示例-深度相等判斷">13.3. 示例: 深度相等判斷</h2>
|
||||
<p>來自 reflect 包的 DeepEqual 對兩個值進行深度相等判斷. DeepEqual 使用內建的 <code>==</code> 操作符對基礎類型進行相等判斷, 對於複合類型則遞歸變量每個基礎類型然後做類似的比較判斷. 因爲它工作在任意的類型上, 甚至對一些不支持 <code>==</code> 操作符的類型也可以工作, 因此在一些測試代碼中被廣泛地使用. 比如下面的代碼是用 DeepEqual 比較兩個字符串數組是否等價.</p>
|
||||
<p>來自reflect包的DeepEqual函數可以對兩個值進行深度相等判斷。DeepEqual函數使用內建的==比較操作符對基礎類型進行相等判斷,對於複合類型則遞歸該變量的每個基礎類型然後做類似的比較判斷。因爲它可以工作在任意的類型上,甚至對於一些不支持==操作運算符的類型也可以工作,因此在一些測試代碼中廣泛地使用該函數。比如下面的代碼是用DeepEqual函數比較兩個字符串數組是否相等。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> TestSplit(t *testing.T) {
|
||||
got := strings.Split(<span class="hljs-string">"a:b:c"</span>, <span class="hljs-string">":"</span>)
|
||||
want := []<span class="hljs-typename">string</span>{<span class="hljs-string">"a"</span>, <span class="hljs-string">"b"</span>, <span class="hljs-string">"c"</span>};
|
||||
<span class="hljs-keyword">if</span> !reflect.DeepEqual(got, want) { <span class="hljs-comment">/* ... */</span> }
|
||||
}
|
||||
</code></pre>
|
||||
<p>盡管 DeepEqual 很方便, 而且可以支持任意的類型, 但是也有不足之處.
|
||||
例如, 它將一個 nil map 和 非 nil 的空的 map 視作不相等,
|
||||
同樣 nil slice 和 非 nil 的空的 slice 也不相等.</p>
|
||||
<p>盡管DeepEqual函數很方便,而且可以支持任意的數據類型,但是它也有不足之處。例如,它將一個nil值的map和非nil值但是空的map視作不相等,同樣nil值的slice 和非nil但是空的slice也視作不相等。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">var</span> a, b []<span class="hljs-typename">string</span> = <span class="hljs-constant">nil</span>, []<span class="hljs-typename">string</span>{}
|
||||
fmt.Println(reflect.DeepEqual(a, b)) <span class="hljs-comment">// "false"</span>
|
||||
|
||||
<span class="hljs-keyword">var</span> c, d <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span> = <span class="hljs-constant">nil</span>, <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>)
|
||||
fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<p>在這里定義一個自己的 Equal 函數用於比較人員的值. 和 DeepEqual 類似的是它也是基於 slice 和 map 的元素進行遞歸比較, 不同之處是它將 nil slice(map類似) 和非 nil 的空 slice 視作相等的值. 基礎部分的比較可以基於反射完成, 和 12.3 章的 Display 實現方法類似. 同樣, 我們頂一個一個內部函數 equal, 用於內部的遞歸比較. 目前不用關心 seen 參數. 對於每一對需要比較的 x 和 y, equal 函數 首先檢測它們是否都有效(或都無效), 然後檢測它們是否是相同的類型. 剩下的部分是一個大的 switch 分支, 用於擁有相同基礎類型的比較. 因爲頁面空間的限製, 我們省略了一些類似的分支.</p>
|
||||
<p>我們希望在這里實現一個自己的Equal函數,用於比較類型的值。和DeepEqual函數類似的地方是它也是基於slice和map的每個元素進行遞歸比較,不同之處是它將nil值的slice(map類似)和非nil值但是空的slice視作相等的值。基礎部分的比較可以基於reflect包完成,和12.3章的Display函數的實現方法類似。同樣,我們也定義了一個內部函數equal,用於內部的遞歸比較。讀者目前不用關心seen參數的具體含義。對於每一對需要比較的x和y,equal函數首先檢測它們是否都有效(或都無效),然後檢測它們是否是相同的類型。剩下的部分是一個鉅大的switch分支,用於相同基礎類型的元素比較。因爲頁面空間的限製,我們省略了一些相似的分支。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/equal
|
||||
<span class="hljs-keyword">func</span> equal(x, y reflect.Value, seen <span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">if</span> !x.IsValid() || !y.IsValid() {
|
||||
@@ -2080,8 +2078,7 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
<span class="hljs-built_in">panic</span>(<span class="hljs-string">"unreachable"</span>)
|
||||
}
|
||||
</code></pre>
|
||||
<p>和前面的建議一樣, 我們不公開使用反射相關的接口,
|
||||
所以導齣的函數需要在內部自己將變量轉爲 reflect.Value 類型.</p>
|
||||
<p>和前面的建議一樣,我們併不公開reflect包相關的接口,所以導齣的函數需要在內部自己將變量轉爲reflect.Value類型。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Equal reports whether x and y are deeply equal.</span>
|
||||
<span class="hljs-keyword">func</span> Equal(x, y <span class="hljs-keyword">interface</span>{}) <span class="hljs-typename">bool</span> {
|
||||
seen := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[comparison]<span class="hljs-typename">bool</span>)
|
||||
@@ -2093,7 +2090,7 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
treflect.Type
|
||||
}
|
||||
</code></pre>
|
||||
<p>爲了確保算法對於循環數據結構也能正常退齣, 我們必鬚記録每次已經比較的變量, 從而避免進入第二次的比較. Equal 函數分配了一組用於比較的結構體, 包含每對比較對象的地址(unsafe.Pointer形式保存)和類型. 我們記録類型的原因是, 有些不同的變量可能對應相同的地址. 例如, 如果 x 和 y 都是數組類型, 那麽 x 和 <code>x[0]</code> 將對應相同的地址, y 和 <code>y[0]</code> 也是對應相同的地址, 這可以用於判斷 對x 和 y 比較 或 x[0] 和 y[0] 的是否進行過了.</p>
|
||||
<p>爲了確保算法對於有環的數據結構也能正常退齣,我們必鬚記録每次已經比較的變量,從而避免進入第二次的比較。Equal函數分配了一組用於比較的結構體,包含每對比較對象的地址(unsafe.Pointer形式保存)和類型。我們要記録類型的原因是,有些不同的變量可能對應相同的地址。例如,如果x和y都是數組類型,那麽x和x[0]將對應相同的地址,y和y[0]也是對應相同的地址,這可以用於區分x與y之間的比較或x[0]與y[0]之間的比較是否進行過了。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// cycle check</span>
|
||||
<span class="hljs-keyword">if</span> x.CanAddr() && y.CanAddr() {
|
||||
xptr := unsafe.Pointer(x.UnsafeAddr())
|
||||
@@ -2108,13 +2105,13 @@ fmt.Println(reflect.DeepEqual(c, d)) <span class="hljs-comment">// "false&q
|
||||
seen[c] = <span class="hljs-constant">true</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>這是 Equal 函數的使用的例子:</p>
|
||||
<p>這是Equal函數用法的例子:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(Equal([]<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>}, []<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>})) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal([]<span class="hljs-typename">string</span>{<span class="hljs-string">"foo"</span>}, []<span class="hljs-typename">string</span>{<span class="hljs-string">"bar"</span>})) <span class="hljs-comment">// "false"</span>
|
||||
fmt.Println(Equal([]<span class="hljs-typename">string</span>(<span class="hljs-constant">nil</span>), []<span class="hljs-typename">string</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal(<span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>(<span class="hljs-constant">nil</span>), <span class="hljs-keyword">map</span>[<span class="hljs-typename">string</span>]<span class="hljs-typename">int</span>{})) <span class="hljs-comment">// "true"</span>
|
||||
</code></pre>
|
||||
<p>它甚至可以處理類似12.3章中導致Display陷入死循環的數據.</p>
|
||||
<p>Equal函數甚至可以處理類似12.3章中導致Display陷入陷入死循環的帶有環的數據。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Circular linked lists a -> b -> a and c -> c.</span>
|
||||
<span class="hljs-keyword">type</span> link <span class="hljs-keyword">struct</span> {
|
||||
value <span class="hljs-typename">string</span>
|
||||
@@ -2128,9 +2125,9 @@ fmt.Println(Equal(c, c)) <span class="hljs-comment">// "true"</span>
|
||||
fmt.Println(Equal(a, b)) <span class="hljs-comment">// "false"</span>
|
||||
fmt.Println(Equal(a, c)) <span class="hljs-comment">// "false"</span>
|
||||
</code></pre>
|
||||
<pre><code>練習 13.1: 定義一個深比較函數, 對於十億以內的數字比較, 忽略類型差異.
|
||||
練習 13.2: 編寫一個函數, 報告其參數是否循環數據結構.
|
||||
</code></pre>
|
||||
<p><strong>練習 13.1:</strong> 定義一個深比較函數,對於十億以內的數字比較,忽略類型差異。</p>
|
||||
<p><strong>練習 13.2:</strong> 編寫一個函數,報告其參數是否循環數據結構。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,19 +2024,35 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="134-通過cgo調用c代碼">13.4. 通過cgo調用C代碼</h2>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動的場景, 或者是從一個C++實現的嵌入式數據庫査詢記録的場景, 或者是使用Fortran實現的一些線性代數庫的場景. C作爲一個通用語言, 很多庫會選擇提供一個C兼容的API, 然後用其他語言實現.</p>
|
||||
<p>在本節中, 我們將構建一個簡易的數據壓縮程序, 通過使用一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具. 這類工具被稱爲外圍函數接口(ffi), 併且cgo也不是Go中唯一的類似工具. SWIG(swig.org) 是類似的另一個被廣泛使用的工具, 它提供了很多複雜特性以支援C++的集成, 但 SWIG 不是這里要討論的主題.</p>
|
||||
<p>在標準庫的 <code>compress/...</code> 子目録有很多流行的壓縮算法的編碼和解碼實現, 包括LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法). 這些包的API的細節有些差異, 但是它們都提供了針對 <code>io.Writer</code> 的壓縮接口, 和提供了針對 <code>io.Reader</code> 的解壓縮接口. 例如:</p>
|
||||
<p>Go程序可能會遇到要訪問C語言的某些硬件驅動函數的場景,或者是從一個C++語言實現的嵌入式數據庫査詢記録的場景,或者是使用Fortran語言實現的一些線性代數庫的場景。C語言作爲一個通用語言,很多庫會選擇提供一個C兼容的API,然後用其他不同的編程語言實現(譯者:Go語言需要也應該擁抱這些鉅大的代碼遺産)。</p>
|
||||
<p>在本節中,我們將構建一個簡易的數據壓縮程序,使用了一個Go語言自帶的叫cgo的用於支援C語言函數調用的工具。這類工具一般被稱爲 <em>foreign-function interfaces</em> (簡稱ffi), 併且在類似工具中cgo也不是唯一的。SWIG( <a href="http://swig.org" target="_blank">http://swig.org</a> )是另一個類似的且被廣泛使用的工具,SWIG提供了很多複雜特性以支援C++的特性,但SWIG併不是我們要討論的主題。</p>
|
||||
<p>在標準庫的<code>compress/...</code>子包有很多流行的壓縮算法的編碼和解碼實現,包括流行的LZW壓縮算法(Unix的compress命令用的算法)和DEFLATE壓縮算法(GNU gzip命令用的算法)。這些包的API的細節雖然有些差異,但是它們都提供了針對 io.Writer類型輸齣的壓縮接口和提供了針對io.Reader類型輸入的解壓縮接口。例如:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">package</span> gzip <span class="hljs-comment">// compress/gzip</span>
|
||||
<span class="hljs-keyword">func</span> NewWriter(w io.Writer) io.WriteCloser
|
||||
<span class="hljs-keyword">func</span> NewReader(r io.Reader) (io.ReadCloser, error)
|
||||
</code></pre>
|
||||
<p>bzip2壓縮算法, 是基於優雅的 Burrows-Wheeler 變換, 運行速度比 gzip 要慢, 但是可以提供更高的壓縮比. 標準庫的 <code>compress/bzip2</code> 包目前還沒有提供 bzip2 算法的壓縮實現. 完全從頭實現是一個繁瑣的工作, 而且 bzip.org 有現成的 libbzip2 開源實現, 文檔齊全而且性能較好,</p>
|
||||
<p>如果C庫比較小, 我們可以用純Go重新實現一遍. 如果我們對性能沒有特殊要求, 我們可以用 <code>os/exec</code> 包的方法將C編寫的應用程序作爲一個子進行運行. 隻有當你需要使用複雜但是性能更高的底層C接口時, 就是使用cgo的場景了. 下面我們將通過一個例子講述cgo的用法.</p>
|
||||
<p>要使用 libbzip2, 我們需要一個 <code>bz_stream</code> 結構體, 用於保持輸入和輸齣緩存.
|
||||
然後有三個函數: BZ2_bzCompressInit 用於初始化緩存, BZ2_bzCompress 用於將輸入緩存的數據壓縮到輸齣緩存, BZ2_bzCompressEnd 用於釋放不需要的緩存.
|
||||
(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的)</p>
|
||||
<p>我們可以在Go代碼中直接調用 BZ2_bzCompressInit 和 BZ2_bzCompressEnd, 但是對於 BZ2_bzCompress, 我們將定義一個C語言的包裝函數, 爲了顯示他是如何完成的. 下面是C代碼, 對應一個獨立的文件.</p>
|
||||
<p>bzip2壓縮算法,是基於優雅的Burrows-Wheeler變換算法,運行速度比gzip要慢,但是可以提供更高的壓縮比。標準庫的compress/bzip2包目前還沒有提供bzip2壓縮算法的實現。完全從頭開始實現是一個壓縮算法是一件繁瑣的工作,而且 <a href="http://bzip.org" target="_blank">http://bzip.org</a> 已經有現成的libbzip2的開源實現,不僅文檔齊全而且性能又好。</p>
|
||||
<p>如果是比較小的C語言庫,我們完全可以用純Go語言重新實現一遍。如果我們對性能也沒有特殊要求的話,我們還可以用os/exec包的方法將C編寫的應用程序作爲一個子進程運行。隻有當你需要使用複雜而且性能更高的底層C接口時,就是使用cgo的場景了(譯註:用os/exec包調用子進程的方法會導致程序運行時依賴那個應用程序)。下面我們將通過一個例子講述cgo的具體用法。</p>
|
||||
<p>譯註:本章采用的代碼都是最新的。因爲之前已經齣版的書中包含的代碼隻能在Go1.5之前使用。從Go1.6開始,Go語言已經明確規定了哪些Go語言指針可以之間傳入C語言函數。新代碼重點是增加了bz2alloc和bz2free的兩個函數,用於bz_stream對象空間的申請和釋放操作。下面是新代碼中增加的註釋,説明這個問題:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// The version of this program that appeared in the first and second</span>
|
||||
<span class="hljs-comment">// printings did not comply with the proposed rules for passing</span>
|
||||
<span class="hljs-comment">// pointers between Go and C, described here:</span>
|
||||
<span class="hljs-comment">// https://github.com/golang/proposal/blob/master/design/12416-cgo-pointers.md</span>
|
||||
<span class="hljs-comment">//</span>
|
||||
<span class="hljs-comment">// The rules forbid a C function like bz2compress from storing 'in'</span>
|
||||
<span class="hljs-comment">// and 'out' (pointers to variables allocated by Go) into the Go</span>
|
||||
<span class="hljs-comment">// variable 's', even temporarily.</span>
|
||||
<span class="hljs-comment">//</span>
|
||||
<span class="hljs-comment">// The version below, which appears in the third printing, has been</span>
|
||||
<span class="hljs-comment">// corrected. To comply with the rules, the bz_stream variable must</span>
|
||||
<span class="hljs-comment">// be allocated by C code. We have introduced two C functions,</span>
|
||||
<span class="hljs-comment">// bz2alloc and bz2free, to allocate and free instances of the</span>
|
||||
<span class="hljs-comment">// bz_stream type. Also, we have changed bz2compress so that before</span>
|
||||
<span class="hljs-comment">// it returns, it clears the fields of the bz_stream that contain</span>
|
||||
<span class="hljs-comment">// pointers to Go variables.</span>
|
||||
</code></pre>
|
||||
<p>要使用libbzip2,我們需要先構建一個bz_stream結構體,用於保持輸入和輸齣緩存。然後有三個函數:BZ2_bzCompressInit用於初始化緩存,BZ2_bzCompress用於將輸入緩存的數據壓縮到輸齣緩存,BZ2_bzCompressEnd用於釋放不需要的緩存。(目前不要擔心包的具體結構, 這個例子的目的就是演示各個部分如何組合在一起的。)</p>
|
||||
<p>我們可以在Go代碼中直接調用BZ2_bzCompressInit和BZ2_bzCompressEnd,但是對於BZ2_bzCompress,我們將定義一個C語言的包裝函數,用它完成眞正的工作。下面是C代碼,對應一個獨立的文件。</p>
|
||||
<pre><code class="lang-C">gopl.io/ch13/bzip
|
||||
|
||||
<span class="hljs-comment">/* This file is gopl.io/ch13/bzip/bzip2.c, */</span>
|
||||
@@ -2052,10 +2068,11 @@
|
||||
<span class="hljs-keyword">int</span> r = BZ2_bzCompress(s, action);
|
||||
*inlen -= s->avail_in;
|
||||
*outlen -= s->avail_out;
|
||||
s->next_in = s->next_out = <span class="hljs-literal">NULL</span>;
|
||||
<span class="hljs-keyword">return</span> r;
|
||||
}
|
||||
</code></pre>
|
||||
<p>現在讓我們轉到Go部分, 第一部分如下所示. 其中 <code>import "C"</code> 的語句是比較特别的. 其實併沒有一個叫 <code>C</code> 的包, 但是這行語句會讓Go構建在編譯之前先運行cgo工具.</p>
|
||||
<p>現在讓我們轉到Go語言部分,第一部分如下所示。其中<code>import "C"</code>的語句是比較特别的。其實併沒有一個叫C的包,但是這行語句會讓Go編譯程序在編譯之前先運行cgo工具。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Package bzip provides a writer that uses bzip2 compression (bzip.org).</span>
|
||||
<span class="hljs-keyword">package</span> bzip
|
||||
|
||||
@@ -2063,8 +2080,11 @@
|
||||
#cgo CFLAGS: -I/usr/include
|
||||
#cgo LDFLAGS: -L/usr/lib -lbz2
|
||||
#include <bzlib.h>
|
||||
#include <stdlib.h>
|
||||
bz_stream* bz2alloc() { return calloc(1, sizeof(bz_stream)); }
|
||||
int bz2compress(bz_stream *s, int action,
|
||||
char *in, unsigned *inlen, char *out, unsigned *outlen);
|
||||
void bz2free(bz_stream* s) { free(s); }
|
||||
*/</span>
|
||||
<span class="hljs-keyword">import</span> <span class="hljs-string">"C"</span>
|
||||
|
||||
@@ -2081,18 +2101,40 @@ int bz2compress(bz_stream *s, int action,
|
||||
|
||||
<span class="hljs-comment">// NewWriter returns a writer for bzip2-compressed streams.</span>
|
||||
<span class="hljs-keyword">func</span> NewWriter(out io.Writer) io.WriteCloser {
|
||||
<span class="hljs-keyword">const</span> (
|
||||
blockSize = <span class="hljs-number">9</span>
|
||||
verbosity = <span class="hljs-number">0</span>
|
||||
workFactor = <span class="hljs-number">30</span>
|
||||
)
|
||||
w := &writer{w: out, stream: <span class="hljs-built_in">new</span>(C.bz_stream)}
|
||||
<span class="hljs-keyword">const</span> blockSize = <span class="hljs-number">9</span>
|
||||
<span class="hljs-keyword">const</span> verbosity = <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">const</span> workFactor = <span class="hljs-number">30</span>
|
||||
w := &writer{w: out, stream: C.bz2alloc()}
|
||||
C.BZ2_bzCompressInit(w.stream, blockSize, verbosity, workFactor)
|
||||
<span class="hljs-keyword">return</span> w
|
||||
}
|
||||
</code></pre>
|
||||
<p>在循環的每次迭代中, 向bz2compress傳入數據的地址和剩餘部分的長度, 還有輸齣緩存 w.outbuf 的地址和容量. 這兩個長度信息通過它們的地址傳入而不是值傳入, 因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值(譯註: 這里的用法有問題, 勘誤已經提到. 具體脩複的方法稍後再補充). 每個塊壓縮後的數據被寫入到底層的 io.Writer.</p>
|
||||
<p>Close 方法和 Write 方法有着類似的結構, 通過一個循環將剩餘的壓縮數據刷新到輸齣緩存.</p>
|
||||
<p>在預處理過程中,cgo工具爲生成一個臨時包用於包含所有在Go語言中訪問的C語言的函數或類型。例如C.bz_stream和C.BZ2_bzCompressInit。cgo工具通過以某種特殊的方式調用本地的C編譯器來發現在Go源文件導入聲明前的註釋中包含的C頭文件中的內容(譯註:<code>import "C"語句前僅捱着的註釋是對應cgo的特殊語法,對應必要的構建參數選項和C語言代碼</code>)。</p>
|
||||
<p>在cgo註釋中還可以包含#cgo指令,用於給C語言工具鏈指定特殊的參數。例如CFLAGS和LDFLAGS分别對應傳給C語言編譯器的編譯參數和鏈接器參數,使它們可以特定目録找到bzlib.h頭文件和libbz2.a庫文件。這個例子假設你已經在/usr目録成功安裝了bzip2庫。如果bzip2庫是安裝在不同的位置,你需要更新這些參數。</p>
|
||||
<p>NewWriter函數通過調用C語言的BZ2_bzCompressInit函數來初始化stream中的緩存。在writer結構中還包括了另一個buffer,用於輸齣緩存。</p>
|
||||
<p>下面是Write方法的實現,返迴成功壓縮數據的大小,主體是一個循環中調用C語言的bz2compress函數實現的。從代碼可以看到,Go程序可以訪問C語言的bz_stream、char和uint類型,還可以訪問bz2compress等函數,甚至可以訪問C語言中像BZ_RUN那樣的宏定義,全部都是以C.x語法訪問。其中C.uint類型和Go語言的uint類型併不相同,卽使它們具有相同的大小也是不同的類型。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> (w *writer) Write(data []<span class="hljs-typename">byte</span>) (<span class="hljs-typename">int</span>, error) {
|
||||
<span class="hljs-keyword">if</span> w.stream == <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-built_in">panic</span>(<span class="hljs-string">"closed"</span>)
|
||||
}
|
||||
<span class="hljs-keyword">var</span> total <span class="hljs-typename">int</span> <span class="hljs-comment">// uncompressed bytes written</span>
|
||||
|
||||
<span class="hljs-keyword">for</span> <span class="hljs-built_in">len</span>(data) > <span class="hljs-number">0</span> {
|
||||
inlen, outlen := C.<span class="hljs-typename">uint</span>(<span class="hljs-built_in">len</span>(data)), C.<span class="hljs-typename">uint</span>(<span class="hljs-built_in">cap</span>(w.outbuf))
|
||||
C.bz2compress(w.stream, C.BZ_RUN,
|
||||
(*C.char)(unsafe.Pointer(&data[<span class="hljs-number">0</span>])), &inlen,
|
||||
(*C.char)(unsafe.Pointer(&w.outbuf)), &outlen)
|
||||
total += <span class="hljs-typename">int</span>(inlen)
|
||||
data = data[inlen:]
|
||||
<span class="hljs-keyword">if</span> _, err := w.w.Write(w.outbuf[:outlen]); err != <span class="hljs-constant">nil</span> {
|
||||
<span class="hljs-keyword">return</span> total, err
|
||||
}
|
||||
}
|
||||
<span class="hljs-keyword">return</span> total, <span class="hljs-constant">nil</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>在循環的每次迭代中,向bz2compress傳入數據的地址和剩餘部分的長度,還有輸齣緩存w.outbuf的地址和容量。這兩個長度信息通過它們的地址傳入而不是值傳入,因爲bz2compress函數可能會根據已經壓縮的數據和壓縮後數據的大小來更新這兩個值。每個塊壓縮後的數據被寫入到底層的io.Writer。</p>
|
||||
<p>Close方法和Write方法有着類似的結構,通過一個循環將剩餘的壓縮數據刷新到輸齣緩存。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// Close flushes the compressed data and closes the stream.</span>
|
||||
<span class="hljs-comment">// It does not close the underlying io.Writer.</span>
|
||||
<span class="hljs-keyword">func</span> (w *writer) Close() error {
|
||||
@@ -2101,6 +2143,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
<span class="hljs-keyword">defer</span> <span class="hljs-keyword">func</span>() {
|
||||
C.BZ2_bzCompressEnd(w.stream)
|
||||
C.bz2free(w.stream)
|
||||
w.stream = <span class="hljs-constant">nil</span>
|
||||
}()
|
||||
<span class="hljs-keyword">for</span> {
|
||||
@@ -2116,9 +2159,9 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>壓縮完成後, Close 用了 defer 確保函數退齣前調用 C.BZ2_bzCompressEnd 釋放輸入和輸齣流的緩存. 此刻 <code>w.stream</code> 指針將不在有效, 我們將它設置爲 nil 以保證安全, 然後在每個方法中增加 nil 檢測, 以防止用戶在關閉後依然錯誤使用相關方法.</p>
|
||||
<p>不僅僅寫是非併發安全的, 甚至併發調用 Close 和 Write 也可能導致C代碼的崩潰. 脩複這個問題是 練習13.3 的內容.</p>
|
||||
<p>下面的bzipper程序是使用我們自己包實現的bzip2壓縮命令. 它的行爲和許多Unix繫統的 bzip2 命令類似.</p>
|
||||
<p>壓縮完成後,Close方法用了defer函數確保函數退齣前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C資源。此刻w.stream指針將不在有效,我們將它設置爲nil以保證安全,然後在每個方法中增加了nil檢測,以防止用戶在關閉後依然錯誤使用相關方法。</p>
|
||||
<p>上面的實現中,不僅僅寫是非併發安全的,甚至併發調用Close和Write方法也可能導致程序的的崩潰。脩複這個問題是練習13.3的內容。</p>
|
||||
<p>下面的bzipper程序,使用我們自己包實現的bzip2壓縮命令。它的行爲和許多Unix繫統的bzip2命令類似。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch13/bzipper
|
||||
|
||||
<span class="hljs-comment">// Bzipper reads input, bzip2-compresses it, and writes it out.</span>
|
||||
@@ -2141,7 +2184,7 @@ int bz2compress(bz_stream *s, int action,
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>在上面的場景中, 我們使用 bzipper 壓縮了 /usr/share/dict/words 繫統自帶的詞典, 從 938,848 字節壓縮到 335,405 字節, 大於是原始大小的三分之一. 然後使用繫統自帶的bunzip2命令進行解壓. 壓縮前後文件的SHA256哈希碼是相同了, 這也説明了我們的壓縮工具是可用的. (如果你的繫統沒有sha256sum命令, 那麽請先按照 練習4.2 實現一個類似的工具)</p>
|
||||
<p>在上面的場景中,我們使用bzipper壓縮了/usr/share/dict/words繫統自帶的詞典,從938,848字節壓縮到335,405字節。大約是原始數據大小的三分之一。然後使用繫統自帶的bunzip2命令進行解壓。壓縮前後文件的SHA256哈希碼是相同了,這也説明了我們的壓縮工具是正確的。(如果你的繫統沒有sha256sum命令,那麽請先按照練習4.2實現一個類似的工具)</p>
|
||||
<pre><code>$ go build gopl.io/ch13/bzipper
|
||||
$ wc -c < /usr/share/dict/words
|
||||
938848
|
||||
@@ -2151,9 +2194,9 @@ $ ./bzipper < /usr/share/dict/words | wc -c
|
||||
335405
|
||||
$ ./bzipper < /usr/share/dict/words | bunzip2 | sha256sum
|
||||
126a4ef38493313edc50b86f90dfdaf7c59ec6c948451eac228f2f3a8ab1a6ed -
|
||||
</code></pre><p>我們演示了將一個C庫鏈接到Go程序. 相反, 將Go編譯爲靜態庫然後鏈接到C程序, 或者將Go編譯爲動態庫然後在C程序中動態加載也都是可行的. 這里我們隻展示的cgo很小的一些方面, 更多的關於內存管理, 指針, 迴調函數, 信號處理, 字符串, errno處理, 終結器, 以及 goroutines 和繫統線程的關繫等, 有很多細節可以討論. 特别是如何將Go的指針傳入C函數的規則也是異常複雜的, 部分的原因在 13.2節 有討論到, 但是在Go1.5中還沒有被明確. 如果要進一步閲讀, 可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始.</p>
|
||||
<p><strong>練習13.3:</strong> 使用 sync.Mutex 以保證 bzip2.writer 在多個 goroutines 中被併發調用是安全的.</p>
|
||||
<p><strong>練習13.4:</strong> 因爲C庫依賴的限製. 使用 <code>os/exec</code> 包啟動 <code>/bin/bzip2</code> 命令作爲一個子進程, 提供一個純Go的 bzip.NewWriter 的替代實現.</p>
|
||||
</code></pre><p>我們演示了如何將一個C語言庫鏈接到Go語言程序。相反, 將Go編譯爲靜態庫然後鏈接到C程序,或者將Go程序編譯爲動態庫然後在C程序中動態加載也都是可行的(譯註:在Go1.5中,Windows繫統的Go語言實現併不支持生成C語言動態庫或靜態庫的特性。不過好消息是,目前已經有人在嚐試解決這個問題,具體請訪問 <a href="https://github.com/golang/go/issues/11058" target="_blank">Issue11058</a> )。這里我們隻展示的cgo很小的一些方面,更多的關於內存管理、指針、迴調函數、中斷信號處理、字符串、errno處理、終結器,以及goroutines和繫統線程的關繫等,有很多細節可以討論。特别是如何將Go語言的指針傳入C函數的規則也是異常複雜的(譯註:簡單來説,要傳入C函數的Go指針指向的數據本身不能包含指針或其他引用類型;併且C函數在返迴後不能繼續持有Go指針;併且在C函數返迴之前,Go指針是被鎖定的,不能導致對應指針數據被移動或棧的調整),部分的原因在13.2節有討論到,但是在Go1.5中還沒有被明確(譯註:Go1.6將會明確cgo中的指針使用規則)。如果要進一步閲讀,可以從 <a href="https://golang.org/cmd/cgo" target="_blank">https://golang.org/cmd/cgo</a> 開始。</p>
|
||||
<p><strong>練習 13.3:</strong> 使用sync.Mutex以保證bzip2.writer在多個goroutines中被併發調用是安全的。</p>
|
||||
<p><strong>練習 13.4:</strong> 因爲C庫依賴的限製。 使用os/exec包啟動/bin/bzip2命令作爲一個子進程,提供一個純Go的bzip.NewWriter的替代實現(譯註:雖然是純Go實現,但是運行時將依賴/bin/bzip2目録,其他操作繫統可能無法運行)。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
|
||||
|
||||
|
||||
<link rel="next" href="../errata.html" />
|
||||
<link rel="next" href="../CONTRIBUTORS.html" />
|
||||
|
||||
|
||||
<link rel="prev" href="../ch13/ch13-04.html" />
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,11 +2024,11 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="135-幾點忠告">13.5. 幾點忠告</h2>
|
||||
<p>我們在前一章結尾的時候, 我們警告要謹慎使用反射. 那些警告同樣適用於本章的 unsafe 包.</p>
|
||||
<p>高級語言使得程序員不用在關繫眞正運行程序的指令細節, 同時也不再需要關註許多如內部布局之類的無關實現細節. 因爲這個絶緣的抽象層, 我們可以編寫安全健壯的, 併且可以運行在不同操作繫統上的具有高度可移植性的程序.</p>
|
||||
<p>但是 unsafe 包, 讓程序員可以透過這個絶緣的抽象層使用使用一些必要的功能, 或者是爲了更高的性能. 代價就是犧牲了可移植性和程序安全, 因此使用 unsafe 是一個危險的行爲. 我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似. 大多數Go程序員可能永遠不會需要直接使用unsafe包. 當然, 永遠都會有一些用 unsafe 包實現會更簡單的場景. 如果確實認爲使用 unsafe 包是最理想的方式, 那麽應該盡可能將它限製較小的范圍, 那樣其他代碼忽略unsafe的影響.</p>
|
||||
<p>現在, 把最後兩章拋入腦後吧. 編寫一些實在的應用. 遠離reflect的unsafe包, 除非你確實需要它們.</p>
|
||||
<p>用Go快樂地編程. 我們希望你能像我們一樣喜歡Go語言.</p>
|
||||
<p>我們在前一章結尾的時候,我們警告要謹慎使用reflect包。那些警告同樣適用於本章的unsafe包。</p>
|
||||
<p>高級語言使得程序員不用在關心眞正運行程序的指令細節,同時也不再需要關註許多如內存布局之類的實現細節。因爲高級語言這個絶緣的抽象層,我們可以編寫安全健壯的,併且可以運行在不同操作繫統上的具有高度可移植性的程序。</p>
|
||||
<p>但是unsafe包,它讓程序員可以透過這個絶緣的抽象層直接使用一些必要的功能,雖然可能是爲了穫得更好的性能。但是代價就是犧牲了可移植性和程序安全,因此使用unsafe包是一個危險的行爲。我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似。大多數Go程序員可能永遠不會需要直接使用unsafe包。當然,也永遠都會有一些需要使用unsafe包實現會更簡單的場景。如果確實認爲使用unsafe包是最理想的方式,那麽應該盡可能將它限製在較小的范圍,那樣其它代碼就忽略unsafe的影響。</p>
|
||||
<p>現在,趕緊將最後兩章拋入腦後吧。編寫一些實實在在的應用是眞理。請遠離reflect的unsafe包,除非你確實需要它們。</p>
|
||||
<p>最後,用Go快樂地編程。我們希望你能像我們一樣喜歡Go語言。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2042,7 +2042,7 @@
|
||||
<a href="../ch13/ch13-04.html" class="navigation navigation-prev " aria-label="Previous page: 通過cgo調用C代碼"><i class="fa fa-angle-left"></i></a>
|
||||
|
||||
|
||||
<a href="../errata.html" class="navigation navigation-next " aria-label="Next page: 勘誤"><i class="fa fa-angle-right"></i></a>
|
||||
<a href="../CONTRIBUTORS.html" class="navigation navigation-next " aria-label="Next page: 附録"><i class="fa fa-angle-right"></i></a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,16 +2024,15 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第13章-底層編程">第13章 底層編程</h1>
|
||||
<p>Go的設計包含了諸多安全策略, 限製了可能導致程序錯誤的用法. 編譯時類型檢査檢測可以發現大多數類型不匹配的變量操作, 例如兩個字符串做減法的錯誤. 字符串, 字典, 切片 和管道等所有的內置類型, 都有嚴格的類型轉換規則.</p>
|
||||
<p>對於無法靜態檢測到的錯誤, 例如數組訪問越界或使用空指針, 動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息. 自動內存管理(垃圾迴收)消除了大部分野指針和內存洩漏的問題.</p>
|
||||
<p>Go的實現刻意隱藏了很多底層細節. 我們無法知道一個結構體的內存布局, 也無法穫取一個運行函數的機器碼, 也無法知道當前的 goroutine 是運行在哪個操作繫統線程上. 事實上, Go的調度器會自己決定是否需要將 goroutine 從一個操作繫統線程轉移到另一個操作繫統線程. 一個指向變量的指針也併沒有展示變量眞實的地址. 因爲垃圾迴收器會根據需要移動變量的位置, 當然對應的也會被自動更新.</p>
|
||||
<p>總的來説, Go語言的這些特殊使得Go程序相比較低級的C語言來説, 更容易預測, 更容易理解, 也不容易崩潰. 通過隱藏底層的細節, 也使得Go程序具有高度的可移植性, 因爲語言的語義在很大程度上是獨立於任何編譯器, 操作繫統和CPU繫統結構的(當然也不完全絶對獨立: 例如CPU字的大小, 某些表達式求值的順序, 還有編譯器實現的一些限製).</p>
|
||||
<p>有時候我們可能會放棄部分語言特性而優先選擇更好的性能優化, 與其他語言編寫的庫互操作, 或者不用純Go語言來實現某些函數.</p>
|
||||
<p>在本章, 我們將展示如何使用 unsafe 包來襬脫通常的規則限製, 如何創建C函數庫的綁定, 以及如何進行繫統調用.</p>
|
||||
<p>本章描述的方法不應該輕易使用. 如果沒有處理好細節, 它們可能導致各種不可預測的隱晦的錯誤, 甚至連本地的C程序員也無法理解. 使用 unsafe 包同時也無法保證與未來版本的兼容性, 因爲在有意無意中會使用很多實現的細節, 而這些實現的細節在未來很可能會改變.</p>
|
||||
<p>unsafe 包的實現比較特殊. 雖然它可以和普通包一樣的導入和使用, 但它實際上是由編譯器實現的. 它提供了一些訪問語言內部特性的方法, 特别是內存布局相關的細節.
|
||||
將這些特别封裝到一個獨立的包中, 是爲在極少數情況下需要使用的時候, 引起人們的註意(它們是不安全的). 此外, 有一些環境因爲安全的因素可能限製這個包的使用.</p>
|
||||
<p>unsafe 包被廣泛地用於比較低級的包, 例如 runtime, os, syscall 還有 net 等, 因爲它們需要和操作繫統密切配合的, 但是普通的程序一般是不需要的.</p>
|
||||
<p>Go語言的設計包含了諸多安全策略,限製了可能導致程序運行齣現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作,例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型,都有嚴格的類型轉換規則。</p>
|
||||
<p>對於無法靜態檢測到的錯誤,例如數組訪問越界或使用空指針,運行時動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息。自動內存管理(垃圾內存自動迴收)可以消除大部分野指針和內存洩漏相關的問題。</p>
|
||||
<p>Go語言的實現刻意隱藏了很多底層細節。我們無法知道一個結構體眞實的內存布局,也無法穫取一個運行時函數對應的機器碼,也無法知道當前的goroutine是運行在哪個操作繫統線程之上。事實上,Go語言的調度器會自己決定是否需要將某個goroutine從一個操作繫統線程轉移到另一個操作繫統線程。一個指向變量的指針也併沒有展示變量眞實的地址。因爲垃圾迴收器可能會根據需要移動變量的內存位置,當然變量對應的地址也會被自動更新。</p>
|
||||
<p>總的來説,Go語言的這些特性使得Go程序相比較低級的C語言來説更容易預測和理解,程序也不容易崩潰。通過隱藏底層的實現細節,也使得Go語言編寫的程序具有高度的可移植性,因爲語言的語義在很大程度上是獨立於任何編譯器實現、操作繫統和CPU繫統結構的(當然也不是完全絶對獨立:例如int等類型就依賴於CPU機器字的大小,某些表達式求值的具體順序,還有編譯器實現的一些額外的限製等)。</p>
|
||||
<p>有時候我們可能會放棄使用部分語言特性而優先選擇更好具有更好性能的方法,例如需要與其他語言編寫的庫互操作,或者用純Go語言無法實現的某些函數。</p>
|
||||
<p>在本章,我們將展示如何使用unsafe包來襬脫Go語言規則帶來的限製,講述如何創建C語言函數庫的綁定,以及如何進行繫統調用。</p>
|
||||
<p>本章提供的方法不應該輕易使用(譯註:屬於黑魔法,雖然可能功能很強大,但是也容易誤傷到自己)。如果沒有處理好細節,它們可能導致各種不可預測的併且隱晦的錯誤,甚至連有經驗的的C語言程序員也無法理解這些錯誤。使用unsafe包的同時也放棄了Go語言保證與未來版本的兼容性的承諾,因爲它必然會在有意無意中會使用很多實現的細節,而這些實現的細節在未來的Go語言中很可能會被改變。</p>
|
||||
<p>要註意的是,unsafe包是一個采用特殊方式實現的包。雖然它可以和普通包一樣的導入和使用,但它實際上是由編譯器實現的。它提供了一些訪問語言內部特性的方法,特别是內存布局相關的細節。將這些特性封裝到一個獨立的包中,是爲在極少數情況下需要使用的時候,同時引起人們的註意(譯註:因爲看包的名字就知道使用unsafe包是不安全的)。此外,有一些環境因爲安全的因素可能限製這個包的使用。</p>
|
||||
<p>不過unsafe包被廣泛地用於比較低級的包, 例如runtime、os、syscall還有net包等,因爲它們需要和操作繫統密切配合,但是對於普通的程序一般是不需要使用unsafe包的。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.1" data-chapter-title="命名" data-filepath="ch2/ch2-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.1" data-chapter-title="命名" data-filepath="ch2/ch2-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.2" data-chapter-title="聲明" data-filepath="ch2/ch2-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.2" data-chapter-title="聲明" data-filepath="ch2/ch2-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.3" data-chapter-title="變量" data-filepath="ch2/ch2-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.3" data-chapter-title="變量" data-filepath="ch2/ch2-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.4" data-chapter-title="賦值" data-filepath="ch2/ch2-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.4" data-chapter-title="賦值" data-filepath="ch2/ch2-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.5" data-chapter-title="類型" data-filepath="ch2/ch2-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.5" data-chapter-title="類型" data-filepath="ch2/ch2-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.6" data-chapter-title="包和文件" data-filepath="ch2/ch2-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.6" data-chapter-title="包和文件" data-filepath="ch2/ch2-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2.7" data-chapter-title="作用域" data-filepath="ch2/ch2-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2.7" data-chapter-title="作用域" data-filepath="ch2/ch2-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="2" data-chapter-title="程序結構" data-filepath="ch2/ch2.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="2" data-chapter-title="程序結構" data-filepath="ch2/ch2.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.1" data-chapter-title="整型" data-filepath="ch3/ch3-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.2" data-chapter-title="浮點數" data-filepath="ch3/ch3-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.3" data-chapter-title="複數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.3" data-chapter-title="複數" data-filepath="ch3/ch3-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.4" data-chapter-title="布爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.4" data-chapter-title="布爾型" data-filepath="ch3/ch3-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2030,7 +2030,6 @@
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "12"</span>
|
||||
fmt.Println(s[<span class="hljs-number">0</span>], s[<span class="hljs-number">7</span>]) <span class="hljs-comment">// "104 119" ('h' and 'w')</span>
|
||||
</code></pre>
|
||||
<p>Attempting to access a byte outside this range results in a panic:</p>
|
||||
<p>如果視圖訪問超齣字符串范圍的字節將會導致panic異常:</p>
|
||||
<pre><code class="lang-Go">c := s[<span class="hljs-built_in">len</span>(s)] <span class="hljs-comment">// panic: index out of range</span>
|
||||
</code></pre>
|
||||
@@ -2093,10 +2092,10 @@ Usage:
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列. 這種編碼方式叫UTF-32或UCS-4, 每個Unicode碼點都使用同樣的大小32bit來表示. 這種方式比較簡單統一, 它會浪費很多存儲空間, 因爲大數據計算機可讀的文本是ASCII字符, 本來每個ASCII字符隻需要8bit或1字節就能表示. 卽使是常用的字符也遠少於65,536個, 也就是説用16bit編碼方式就能表達常用字符. 但是, 還有更好的編碼方法嗎?</p>
|
||||
<h3 id="353-utf8">3.5.3. UTF-8</h3>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼. UTF8編碼由Go語言之父 Ken Thompson 和 Rob Pike 共同發明, 現在已經是Unicode的標準. UTF8使用1到4個字節來表示每個Unicode碼點符號, ASCII部分字符隻使用1個字節, 常用字符部分使用2或3個字節. 每個符號編碼後第一個字節的高端bit位用於表示總共有多少個字節. 如果第一個字節的高端bit爲0, 則表示對應7bit的ASCII字符, 每個字符一個字節, 和傳統的ASCII編碼兼容. 如果第一個字節的高端bit是110, 則説明需要2個字節; 後續的每個高端bit都以10開頭. 更大的Unicode碼點也是采用類似的策略處理.</p>
|
||||
<pre><code>0xxxxxx runes 0-127 (ASCII)
|
||||
11xxxxx 10xxxxxx 128-2047 (values <128 unused)
|
||||
110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
|
||||
1110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
|
||||
<pre><code>0xxxxxxx runes 0-127 (ASCII)
|
||||
110xxxxx 10xxxxxx 128-2047 (values <128 unused)
|
||||
1110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
|
||||
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符, 但是UTF8穫得了很多額外的優點. 首先UTF8編碼比較緊湊, 兼容ASCII, 併且可以自動同步: 它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置. 它也是一個前綴編碼, 所以當從左向右解碼時不會有任何歧義也併不需要向前査看. 沒有任何字符的編碼是其它字符編碼的子串, 或是其它編碼序列的字串, 因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可, 不用擔心前後的上下文會對蒐索結果産生榦擾. 同時UTF8編碼的順序和Unicode碼點的順序一致, 因此可以直接排序UTF8編碼序列. 同業也沒有嵌入的NUL(0)字節, 可以很好地兼容那些使用NUL作爲字符串結尾的編程語言.</p>
|
||||
<p>Go的源文件采用UTF8編碼, 併且Go處理UTF8編碼的文本也很齣色. unicode 包提供了諸多處理 rune 字符相關功能的函數函數(區分字母和數組, 或者是字母的大寫和小寫轉換等), unicode/utf8 包了提供了rune 字符序列的UTF8編碼和解碼的功能.</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入, 併且很多字符有着相似的結構; 有一些甚至是不可見的字符. Go字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符. 有兩種形式, \uhhhh 對應16bit的碼點值, \Uhhhhhhhh 對應32bit的碼點值, 其中h是一個十六進製數字; 一般很少需要使用32bit的形式. 每一個對應碼點的UTF8編碼. 例如: 下面的字母串面值都表示相同的值:</p>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.6" data-chapter-title="常量" data-filepath="ch3/ch3-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3" data-chapter-title="基礎數據類型" data-filepath="ch3/ch3.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.1" data-chapter-title="數組" data-filepath="ch4/ch4-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.1" data-chapter-title="數組" data-filepath="ch4/ch4-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.2" data-chapter-title="切片" data-filepath="ch4/ch4-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.2" data-chapter-title="切片" data-filepath="ch4/ch4-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.3" data-chapter-title="字典" data-filepath="ch4/ch4-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.3" data-chapter-title="字典" data-filepath="ch4/ch4-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.4" data-chapter-title="結構體" data-filepath="ch4/ch4-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.4" data-chapter-title="結構體" data-filepath="ch4/ch4-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.5" data-chapter-title="JSON" data-filepath="ch4/ch4-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.5" data-chapter-title="JSON" data-filepath="ch4/ch4-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4.6" data-chapter-title="文本和HTML模闆" data-filepath="ch4/ch4-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="4" data-chapter-title="複合數據類型" data-filepath="ch4/ch4.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="4" data-chapter-title="複合數據類型" data-filepath="ch4/ch4.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.1" data-chapter-title="函數聲明" data-filepath="ch5/ch5-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.1" data-chapter-title="函數聲明" data-filepath="ch5/ch5-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.2" data-chapter-title="遞歸" data-filepath="ch5/ch5-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.2" data-chapter-title="遞歸" data-filepath="ch5/ch5-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.3" data-chapter-title="多返迴值" data-filepath="ch5/ch5-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.3" data-chapter-title="多返迴值" data-filepath="ch5/ch5-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.4" data-chapter-title="錯誤" data-filepath="ch5/ch5-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.4" data-chapter-title="錯誤" data-filepath="ch5/ch5-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.5" data-chapter-title="函數值" data-filepath="ch5/ch5-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.5" data-chapter-title="函數值" data-filepath="ch5/ch5-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.6" data-chapter-title="匿名函數" data-filepath="ch5/ch5-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.6" data-chapter-title="匿名函數" data-filepath="ch5/ch5-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.7" data-chapter-title="可變參數" data-filepath="ch5/ch5-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.7" data-chapter-title="可變參數" data-filepath="ch5/ch5-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.8" data-chapter-title="Deferred函數" data-filepath="ch5/ch5-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.8" data-chapter-title="Deferred函數" data-filepath="ch5/ch5-08.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.9" data-chapter-title="Panic異常" data-filepath="ch5/ch5-09.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.9" data-chapter-title="Panic異常" data-filepath="ch5/ch5-09.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5.10" data-chapter-title="Recover捕穫異常" data-filepath="ch5/ch5-10.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5.10" data-chapter-title="Recover捕穫異常" data-filepath="ch5/ch5-10.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="5" data-chapter-title="函數" data-filepath="ch5/ch5.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="5" data-chapter-title="函數" data-filepath="ch5/ch5.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.1" data-chapter-title="方法聲明" data-filepath="ch6/ch6-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.1" data-chapter-title="方法聲明" data-filepath="ch6/ch6-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.2" data-chapter-title="基於指針對象的方法" data-filepath="ch6/ch6-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.2" data-chapter-title="基於指針對象的方法" data-filepath="ch6/ch6-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.3" data-chapter-title="通過嵌入結構體來擴展類型" data-filepath="ch6/ch6-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.3" data-chapter-title="通過嵌入結構體來擴展類型" data-filepath="ch6/ch6-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.4" data-chapter-title="方法值和方法表達式" data-filepath="ch6/ch6-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.4" data-chapter-title="方法值和方法表達式" data-filepath="ch6/ch6-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.5" data-chapter-title="示例: Bit數組" data-filepath="ch6/ch6-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.5" data-chapter-title="示例: Bit數組" data-filepath="ch6/ch6-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6.6" data-chapter-title="封裝" data-filepath="ch6/ch6-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6.6" data-chapter-title="封裝" data-filepath="ch6/ch6-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="6" data-chapter-title="方法" data-filepath="ch6/ch6.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="6" data-chapter-title="方法" data-filepath="ch6/ch6.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.1" data-chapter-title="接口是合約" data-filepath="ch7/ch7-01.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.1" data-chapter-title="接口是合約" data-filepath="ch7/ch7-01.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2042,17 +2042,17 @@
|
||||
併不是一個文件類型盡管它在某種意義上和文件類型相似。</p>
|
||||
<p>卽使Fprintf函數中的第一個參數也不是一個文件類型。它是io.Writer類型這是一個接口類型定義如下:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">package</span> io
|
||||
<span class="hljs-comment">// Writer is the interface that wraps the basic Write method.</span>
|
||||
<span class="hljs-keyword">type</span> Writer <span class="hljs-keyword">interface</span> {
|
||||
<span class="hljs-comment">// Writer is the interface that wraps the basic Write method.</span>
|
||||
<span class="hljs-keyword">type</span> Writer <span class="hljs-keyword">interface</span> {
|
||||
<span class="hljs-comment">// Write writes len(p) bytes from p to the underlying data stream.</span>
|
||||
<span class="hljs-comment">// It returns the number of bytes written from p (0 <= n <= len(p))</span>
|
||||
<span class="hljs-comment">// and any error encountered that caused the write to stop early.</span>
|
||||
<span class="hljs-comment">// Write must return a non-nil error if it returns n < len(p).</span>
|
||||
<span class="hljs-comment">// Write must not modify the slice data, even temporarily.</span>
|
||||
<span class="hljs-comment">//</span>
|
||||
<span class="hljs-comment">// Implementations must not retain p.</span>
|
||||
Write(p []<span class="hljs-typename">byte</span>) (n <span class="hljs-typename">int</span>, err error)
|
||||
}
|
||||
<span class="hljs-comment">// Write writes len(p) bytes from p to the underlying data stream.</span>
|
||||
<span class="hljs-comment">// It returns the number of bytes written from p (0 <= n <= len(p))</span>
|
||||
<span class="hljs-comment">// and any error encountered that caused the write to stop early.</span>
|
||||
<span class="hljs-comment">// Write must return a non-nil error if it returns n < len(p).</span>
|
||||
<span class="hljs-comment">// Write must not modify the slice data, even temporarily.</span>
|
||||
<span class="hljs-comment">//</span>
|
||||
<span class="hljs-comment">// Implementations must not retain p.</span>
|
||||
Write(p []<span class="hljs-typename">byte</span>) (n <span class="hljs-typename">int</span>, err error)
|
||||
</code></pre>
|
||||
<p>io.Writer類型定義了函數Fprintf和這個函數調用者之間的約定。一方面這個約定需要調用者提供具體類型的值就像*os.File和*bytes.Buffer,這些類型都有一個特定籤名和行爲的Write的函數。另一方面這個約定保證了Fprintf接受任何滿足io.Writer接口的值都可以工作。Fprintf函數可能沒有假定寫入的是一個文件或是一段內存,而是寫入一個可以調用Write函數的值。</p>
|
||||
<p>因爲fmt.Fprintf函數沒有對具體操作的值做任何假設而是僅僅通過io.Writer接口的約定來保證行爲,所以第一個參數可以安全地傳入一個任何具體類型的值隻需要滿足io.Writer接口。一個類型可以自由的使用另一個滿足相同接口的類型來進行替換被稱作可替換性(LSP里氏替換)。這是一個面向對象的特徵。</p>
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.2" data-chapter-title="接口類型" data-filepath="ch7/ch7-02.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.2" data-chapter-title="接口類型" data-filepath="ch7/ch7-02.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.3" data-chapter-title="實現接口的條件" data-filepath="ch7/ch7-03.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.3" data-chapter-title="實現接口的條件" data-filepath="ch7/ch7-03.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.4" data-chapter-title="flag.Value接口" data-filepath="ch7/ch7-04.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.4" data-chapter-title="flag.Value接口" data-filepath="ch7/ch7-04.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.5" data-chapter-title="接口值" data-filepath="ch7/ch7-05.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.5" data-chapter-title="接口值" data-filepath="ch7/ch7-05.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.6" data-chapter-title="sort.Interface接口" data-filepath="ch7/ch7-06.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.6" data-chapter-title="sort.Interface接口" data-filepath="ch7/ch7-06.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.7" data-chapter-title="http.Handler接口" data-filepath="ch7/ch7-07.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.7" data-chapter-title="http.Handler接口" data-filepath="ch7/ch7-07.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.8" data-chapter-title="error接口" data-filepath="ch7/ch7-08.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.8" data-chapter-title="error接口" data-filepath="ch7/ch7-08.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.9" data-chapter-title="示例: 表達式求值" data-filepath="ch7/ch7-09.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.9" data-chapter-title="示例: 表達式求值" data-filepath="ch7/ch7-09.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.10" data-chapter-title="類型斷言" data-filepath="ch7/ch7-10.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.10" data-chapter-title="類型斷言" data-filepath="ch7/ch7-10.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="7.11" data-chapter-title="基於類型斷言識别錯誤類型" data-filepath="ch7/ch7-11.md" data-basepath=".." data-revision="Mon Dec 21 2015 12:51:02 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="7.11" data-chapter-title="基於類型斷言識别錯誤類型" data-filepath="ch7/ch7-11.md" data-basepath=".." data-revision="Thu Dec 24 2015 14:42:02 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -1975,16 +1975,16 @@
|
||||
|
||||
</li>
|
||||
|
||||
<li class="chapter " data-level="14" data-path="errata.html">
|
||||
<li class="chapter " data-level="14" data-path="CONTRIBUTORS.html">
|
||||
|
||||
|
||||
<a href="../errata.html">
|
||||
<a href="../CONTRIBUTORS.html">
|
||||
|
||||
<i class="fa fa-check"></i>
|
||||
|
||||
<b>14.</b>
|
||||
|
||||
勘誤
|
||||
附録
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user