這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。歡迎來到 [Golang 系列教程](/subject/2)的第 15 個教程。### 什麼是指標?指標是一種儲存變數記憶體位址(Memory Address)的變數。![指標](https://raw.githubusercontent.com/studygolang/gctt-images/master/golang-series/pointer-explained.png "指標") 如所示,變數 `b` 的值為 `156`,而 `b` 的記憶體位址為 `0x1040a124`。變數 `a` 儲存了 `b` 的地址。我們就稱 `a` 指向了 `b`。### 指標的聲明指標變數的類型為 **`*T`**,該指標指向一個 **T** 類型的變數。接下來我們寫點代碼。```gopackage mainimport ( "fmt")func main() { b := 255 var a *int = &b fmt.Printf("Type of a is %T\n", a) fmt.Println("address of b is", a)}```[線上運行程式](https://play.golang.org/p/A4vmlgxAy8)**&** 操作符用於擷取變數的地址。上面程式的第 9 行我們把 `b` 的地址賦值給 **`*int`** 類型的 `a`。我們稱 `a` 指向了 `b`。當我們列印 `a` 的值時,會列印出 `b` 的地址。程式將輸出:```Type of a is *int address of b is 0x1040a124 ```由於 b 可能處於記憶體的任何位置,你應該會得到一個不同的地址。### 指標的零值(Zero Value)指標的零值是 `nil`。```gopackage mainimport ( "fmt")func main() { a := 25 var b *int if b == nil { fmt.Println("b is", b) b = &a fmt.Println("b after initialization is", b) }}```[線上運行程式](https://play.golang.org/p/yAeGhzgQE1)上面的程式中,`b` 初始化為 `nil`,接著將 `a` 的地址賦值給 `b`。程式會輸出:```b is <nil> b after initialisation is 0x1040a124 ```### 指標的解引用指標的解引用可以擷取指標所指向的變數的值。將 `a` 解引用的文法是 `*a`。通過下面的代碼,可以看到如何使用解引用。```gopackage main import ( "fmt")func main() { b := 255 a := &b fmt.Println("address of b is", a) fmt.Println("value of b is", *a)}```[線上運行程式](https://play.golang.org/p/m5pNbgFwbM)在上面程式的第 10 行,我們將 `a` 解引用,並列印了它的值。不出所料,我們會列印出 `b` 的值。程式會輸出:```address of b is 0x1040a124 value of b is 255 ```我們再編寫一個程式,用指標來修改 b 的值。```gopackage mainimport ( "fmt")func main() { b := 255 a := &b fmt.Println("address of b is", a) fmt.Println("value of b is", *a) *a++ fmt.Println("new value of b is", b)}```[線上運行程式](https://play.golang.org/p/cdmvlpBNmb)在上面程式的第 12 行中,我們把 `a` 指向的值加 1,由於 `a` 指向了 `b`,因此 `b` 的值也發生了同樣的改變。於是 `b` 的值變為 256。程式會輸出:```address of b is 0x1040a124 value of b is 255 new value of b is 256 ```### 向函數傳遞指標參數```gopackage mainimport ( "fmt")func change(val *int) { *val = 55}func main() { a := 58 fmt.Println("value of a before function call is",a) b := &a change(b) fmt.Println("value of a after function call is", a)}```[線上運行程式](https://play.golang.org/p/3n2nHRJJqn)在上面程式中的第 14 行,我們向函數 `change` 傳遞了指標變數 `b`,而 `b` 儲存了 `a` 的地址。程式的第 8 行在 `change` 函數內使用解引用,修改了 a 的值。該程式會輸出:```value of a before function call is 58 value of a after function call is 55 ```### 不要向函數傳遞數組的指標,而應該使用切片假如我們想要在函數內修改一個數組,並希望調用函數的地方也能得到修改後的數組,一種解決方案是把一個指向數組的指標傳遞給這個函數。```gopackage mainimport ( "fmt")func modify(arr *[3]int) { (*arr)[0] = 90}func main() { a := [3]int{89, 90, 91} modify(&a) fmt.Println(a)}```[線上運行程式](https://play.golang.org/p/lOIznCbcvs)在上面程式的第 13 行中,我們將數組的地址傳遞給了 `modify` 函數。在第 8 行,我們在 `modify` 函數裡把 `arr` 解引用,並將 `90` 賦值給這個數組的第一個元素。程式會輸出 `[90 90 91]`。**`a[x]` 是 `(*a)[x]` 的簡寫形式,因此上面代碼中的 `(*arr)[0]` 可以替換為 `arr[0]`**。下面我們用簡寫形式重寫以上代碼。```gopackage mainimport ( "fmt")func modify(arr *[3]int) { arr[0] = 90}func main() { a := [3]int{89, 90, 91} modify(&a) fmt.Println(a)}```[線上運行程式](https://play.golang.org/p/k7YR0EUE1G)該程式也會輸出 `[90 90 91]`。**這種方式向函數傳遞一個數組指標參數,並在函數內修改數組。儘管它是有效,但卻不是 Go 語言慣用的實現方式。我們最好使用[切片](https://golangbot.com/arrays-and-slices/)來處理。**接下來我們用[切片](https://golangbot.com/arrays-and-slices/)來重寫之前的代碼。```gopackage mainimport ( "fmt")func modify(sls []int) { sls[0] = 90}func main() { a := [3]int{89, 90, 91} modify(a[:]) fmt.Println(a)}```[線上運行程式](https://play.golang.org/p/rRvbvuI67W)在上面程式的第 13 行,我們將一個切片傳遞給了 `modify` 函數。在 `modify` 函數中,我們把切片的第一個元素修改為 `90`。程式也會輸出 `[90 90 91]`。**所以別再傳遞數組指標了,而是使用切片吧**。上面的代碼更加簡潔,也更符合 Go 語言的習慣。### Go 不支援指標運算Go 並不支援其他語言(例如 C)中的指標運算。```gopackage mainfunc main() { b := [...]int{109, 110, 111} p := &b p++}```[線上運行程式](https://play.golang.org/p/WRaj4pkqRD)上面的程式會拋出編譯錯誤:**`main.go:6: invalid operation: p++ (non-numeric type *[3]int)`**。我在 [github](https://github.com/golangbot/pointers) 上建立了一個程式,涵蓋了所有我們討論過的內容。關於指標的介紹到此結束。祝您愉快。**上一教程 - [字串](https://studygolang.com/articles/12261)****下一教程 - [結構體](https://studygolang.com/articles/12263)**
via: https://golangbot.com/pointers/
作者:Nick Coghlan 譯者:Noluye 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽
2666 次點擊