mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-01-14 11:17:15 +08:00
rebuild
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,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="Fri Dec 25 2015 12:32:44 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="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2024,7 +2028,7 @@
|
||||
<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>Sizeof函數返迴的大小隻包括數據結構中固定的部分,例如字符串對應結構體中的指針和字符串長度部分,但是併不包含指針指向的字符串的內容。Go語言中非聚合類型通常有一個固定的大小,盡管在不同工具鏈下生成的實際大小可能會有所不同。考慮到可移植性,引用類型或包含引用類型的大小在32位平颱上是4個字節,在64位平颱上是8個字節。</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>
|
||||
@@ -2083,7 +2087,7 @@ fmt.Println(unsafe.Sizeof(<span class="hljs-typename">float64</span>(<span class
|
||||
<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>關於內存地址對齊算法的細節超齣了本書的范圍,也不是每一個結構體都需要擔心這個問題,不過有效的包裝可以使數據結構更加緊湊(譯註:未來的Go語言編譯器應該會默認優化結構體的順序,當然用於應該也能夠指定具體的內存布局,相同討論請參考 <a href="https://github.com/golang/go/issues/10014" target="_blank">Issue10014</a> ),內存使用率和性能都可能會受益。</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>
|
||||
@@ -2137,7 +2141,7 @@ Sizeof(x.c) = 24 Alignof(x.c) = 8 Offsetof(x.c) = 8
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,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="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.2" data-chapter-title="unsafe.Pointer" data-filepath="ch13/ch13-02.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,7 +2024,7 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="132-unsafepointer">13.2. unsafe.Pointer</h2>
|
||||
<p>大多數指針類型會寫成<code>*T</code>,表示是“一個指向T類型變量的指針”。unsafe.Pointer是特别定義的一種指針類型(譯註:類似C語言中的<code>void*</code>類型的指針),它可以包含任意類型變量的地址。當然,我們不可以直接通過<code>*p</code>來穫取unsafe.Pointer指針指向的眞實變量的值,因爲我們併不知道變量的具體類型。和普通指針一樣,unsafe.Pointer指針也是可以比較的,併且支持和nil常量比較判斷是否爲空指針。</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
|
||||
|
||||
@@ -2096,7 +2100,7 @@ pb := (*<span class="hljs-typename">int16</span>)(unsafe.Pointer(tmp))
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.3" data-chapter-title="示例: 深度相等判斷" data-filepath="ch13/ch13-03.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2074,7 +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包相關的接口,所以導齣的函數需要在內部自己將變量轉爲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>)
|
||||
@@ -2086,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和x[0]將對應相同的地址,y和y[0]也是對應相同的地址,這可以用於區分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())
|
||||
@@ -2154,7 +2158,7 @@ fmt.Println(Equal(a, c)) <span class="hljs-comment">// "false"</span>
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,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="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.4" data-chapter-title="通過cgo調用C代碼" data-filepath="ch13/ch13-04.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2022,14 +2026,14 @@
|
||||
<h2 id="134-通過cgo調用c代碼">13.4. 通過cgo調用C代碼</h2>
|
||||
<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>
|
||||
<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要慢,但是可以提供更高的壓縮比。標準庫的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>
|
||||
<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>
|
||||
@@ -2047,7 +2051,7 @@
|
||||
<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>要使用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
|
||||
|
||||
@@ -2107,7 +2111,7 @@ void bz2free(bz_stream* s) { free(s); }
|
||||
</code></pre>
|
||||
<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>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> {
|
||||
@@ -2129,8 +2133,8 @@ void bz2free(bz_stream* s) { free(s); }
|
||||
<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>
|
||||
<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 {
|
||||
@@ -2155,7 +2159,7 @@ void bz2free(bz_stream* s) { free(s); }
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p>壓縮完成後,Close方法用了defer函數確保函數退齣前調用C.BZ2_bzCompressEnd和C.bz2free釋放相關的C语言运行时資源。此刻w.stream指針將不再有效,我們將它設置爲nil以保證安全,然後在每個方法中增加了nil檢測,以防止用戶在關閉後依然錯誤使用相關方法。</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
|
||||
@@ -2224,7 +2228,7 @@ $ ./bzipper < /usr/share/dict/words | bunzip2 | sha256sum
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13.5" data-chapter-title="幾點忠告" data-filepath="ch13/ch13-05.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2022,7 +2026,7 @@
|
||||
<h2 id="135-幾點忠告">13.5. 幾點忠告</h2>
|
||||
<p>我們在前一章結尾的時候,我們警告要謹慎使用reflect包。那些警告同樣適用於本章的unsafe包。</p>
|
||||
<p>高級語言使得程序員不用在關心眞正運行程序的指令細節,同時也不再需要關註許多如內存布局之類的實現細節。因爲高級語言這個絶緣的抽象層,我們可以編寫安全健壯的,併且可以運行在不同操作繫統上的具有高度可移植性的程序。</p>
|
||||
<p>但是unsafe包,它讓程序員可以透過這個絶緣的抽象層直接使用一些必要的功能,雖然可能是爲了穫得更好的性能。但是代價就是犧牲了可移植性和程序安全,因此使用unsafe包是一個危險的行爲。我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似。大多數Go程序員可能永遠不會需要直接使用unsafe包。當然,也永遠都會有一些需要使用unsafe包實現會更簡單的場景。如果確實認爲使用unsafe包是最理想的方式,那麽應該盡可能將它限製在較小的范圍,那樣其它代碼就忽略unsafe的影響。</p>
|
||||
<p>但是unsafe包,它讓程序員可以透過這個絶緣的抽象層直接使用一些必要的功能,雖然可能是爲了獲得更好的性能。但是代價就是犧牲了可移植性和程序安全,因此使用unsafe包是一個危險的行爲。我們對何時以及如何使用unsafe包的建議和我們在11.5節提到的Knuth對過早優化的建議類似。大多數Go程序員可能永遠不會需要直接使用unsafe包。當然,也永遠都會有一些需要使用unsafe包實現會更簡單的場景。如果確實認爲使用unsafe包是最理想的方式,那麽應該盡可能將它限製在較小的范圍,那樣其它代碼就忽略unsafe的影響。</p>
|
||||
<p>現在,趕緊將最後兩章拋入腦後吧。編寫一些實實在在的應用是眞理。請遠離reflect的unsafe包,除非你確實需要它們。</p>
|
||||
<p>最後,用Go快樂地編程。我們希望你能像我們一樣喜歡Go語言。</p>
|
||||
|
||||
@@ -2056,7 +2060,7 @@
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="13" data-chapter-title="底層編程" data-filepath="ch13/ch13.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,9 +2024,9 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h1 id="第13章-底層編程">第13章 底層編程</h1>
|
||||
<p>Go語言的設計包含了諸多安全策略,限製了可能導致程序運行齣現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作,例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型,都有嚴格的類型轉換規則。</p>
|
||||
<p>Go語言的設計包含了諸多安全策略,限製了可能導致程序運行出現錯誤的用法。編譯時類型檢査檢査可以發現大多數類型不匹配的操作,例如兩個字符串做減法的錯誤。字符串、map、slice和chan等所有的內置類型,都有嚴格的類型轉換規則。</p>
|
||||
<p>對於無法靜態檢測到的錯誤,例如數組訪問越界或使用空指針,運行時動態檢測可以保證程序在遇到問題的時候立卽終止併打印相關的錯誤信息。自動內存管理(垃圾內存自動迴收)可以消除大部分野指針和內存洩漏相關的問題。</p>
|
||||
<p>Go語言的實現刻意隱藏了很多底層細節。我們無法知道一個結構體眞實的內存布局,也無法穫取一個運行時函數對應的機器碼,也無法知道當前的goroutine是運行在哪個操作繫統線程之上。事實上,Go語言的調度器會自己決定是否需要將某個goroutine從一個操作繫統線程轉移到另一個操作繫統線程。一個指向變量的指針也併沒有展示變量眞實的地址。因爲垃圾迴收器可能會根據需要移動變量的內存位置,當然變量對應的地址也會被自動更新。</p>
|
||||
<p>Go語言的實現刻意隱藏了很多底層細節。我們無法知道一個結構體眞實的內存布局,也無法獲取一個運行時函數對應的機器碼,也無法知道當前的goroutine是運行在哪個操作繫統線程之上。事實上,Go語言的調度器會自己決定是否需要將某個goroutine從一個操作繫統線程轉移到另一個操作繫統線程。一個指向變量的指針也併沒有展示變量眞實的地址。因爲垃圾迴收器可能會根據需要移動變量的內存位置,當然變量對應的地址也會被自動更新。</p>
|
||||
<p>總的來説,Go語言的這些特性使得Go程序相比較低級的C語言來説更容易預測和理解,程序也不容易崩潰。通過隱藏底層的實現細節,也使得Go語言編寫的程序具有高度的可移植性,因爲語言的語義在很大程度上是獨立於任何編譯器實現、操作繫統和CPU繫統結構的(當然也不是完全絶對獨立:例如int等類型就依賴於CPU機器字的大小,某些表達式求值的具體順序,還有編譯器實現的一些額外的限製等)。</p>
|
||||
<p>有時候我們可能會放棄使用部分語言特性而優先選擇更好具有更好性能的方法,例如需要與其他語言編寫的庫互操作,或者用純Go語言無法實現的某些函數。</p>
|
||||
<p>在本章,我們將展示如何使用unsafe包來襬脫Go語言規則帶來的限製,講述如何創建C語言函數庫的綁定,以及如何進行繫統調用。</p>
|
||||
@@ -2060,7 +2064,7 @@
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user