Go 經典入門系列 15:指針
歡迎來到 Golang 系列教程的第 15 個教程。
什么是指針?
指針是一種存儲變量內存地址(Memory Address)的變量。

如上圖所示,變量 b 的值為 156,而 b 的內存地址為 0x1040a124。變量 a 存儲了 b 的地址。我們就稱 a 指向了 b。
指針的聲明
指針變量的類型為 *T,該指針指向一個 T 類型的變量。
接下來我們寫點代碼。
package?main
import?(
????"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)
}
在線運行程序[1]
& 操作符用于獲取變量的地址。上面程序的第 9 行我們把 b 的地址賦值給 *int 類型的 a。我們稱 a 指向了 b。當我們打印 a 的值時,會打印出 b 的地址。程序將輸出:
Type?of?a?is?*int
address?of?b?is?0x1040a124
由于 b 可能處于內存的任何位置,你應該會得到一個不同的地址。
指針的零值(Zero Value)
指針的零值是 nil。
package?main
import?(
????"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)
????}
}
在線運行程序[2]
上面的程序中,b 初始化為 nil,接著將 a 的地址賦值給 b。程序會輸出:
b?is?
b?after?initialisation?is?0x1040a124
指針的解引用
指針的解引用可以獲取指針所指向的變量的值。將 a 解引用的語法是 *a。
通過下面的代碼,可以看到如何使用解引用。
package?main
import?(
????"fmt"
)
func?main()?{
????b?:=?255
????a?:=?&b
????fmt.Println("address?of?b?is",?a)
????fmt.Println("value?of?b?is",?*a)
}
在線運行程序[3]
在上面程序的第 10 行,我們將 a 解引用,并打印了它的值。不出所料,我們會打印出 b 的值。程序會輸出:
address?of?b?is?0x1040a124
value?of?b?is?255
我們再編寫一個程序,用指針來修改 b 的值。
package?main
import?(
????"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)
}
在線運行程序[4]
在上面程序的第 12 行中,我們把 a 指向的值加 1,由于 a 指向了 b,因此 b 的值也發(fā)生了同樣的改變。于是 b 的值變?yōu)?256。程序會輸出:
address?of?b?is?0x1040a124
value?of?b?is?255
new?value?of?b?is?256
向函數(shù)傳遞指針參數(shù)
package?main
import?(
????"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)
}
在線運行程序[5]
在上面程序中的第 14 行,我們向函數(shù) change 傳遞了指針變量 b,而 b 存儲了 a 的地址。程序的第 8 行在 change 函數(shù)內使用解引用,修改了 a 的值。該程序會輸出:
value?of?a?before?function?call?is?58
value?of?a?after?function?call?is?55
不要向函數(shù)傳遞數(shù)組的指針,而應該使用切片
假如我們想要在函數(shù)內修改一個數(shù)組,并希望調用函數(shù)的地方也能得到修改后的數(shù)組,一種解決方案是把一個指向數(shù)組的指針傳遞給這個函數(shù)。
package?main
import?(
????"fmt"
)
func?modify(arr?*[3]int)?{
????(*arr)[0]?=?90
}
func?main()?{
????a?:=?[3]int{89,?90,?91}
????modify(&a)
????fmt.Println(a)
}
在線運行程序[6]
在上面程序的第 13 行中,我們將數(shù)組的地址傳遞給了 modify 函數(shù)。在第 8 行,我們在 modify 函數(shù)里把 arr 解引用,并將 90 賦值給這個數(shù)組的第一個元素。程序會輸出 [90 90 91]。
a[x] 是 (*a)[x] 的簡寫形式,因此上面代碼中的 (*arr)[0] 可以替換為 arr[0]。下面我們用簡寫形式重寫以上代碼。
package?main
import?(
????"fmt"
)
func?modify(arr?*[3]int)?{
????arr[0]?=?90
}
func?main()?{
????a?:=?[3]int{89,?90,?91}
????modify(&a)
????fmt.Println(a)
}
在線運行程序[7]
該程序也會輸出 [90 90 91]。
這種方式向函數(shù)傳遞一個數(shù)組指針參數(shù),并在函數(shù)內修改數(shù)組。盡管它是有效的,但卻不是 Go 語言慣用的實現(xiàn)方式。我們最好使用切片[8]來處理。
接下來我們用切片[9]來重寫之前的代碼。
package?main
import?(
????"fmt"
)
func?modify(sls?[]int)?{
????sls[0]?=?90
}
func?main()?{
????a?:=?[3]int{89,?90,?91}
????modify(a[:])
????fmt.Println(a)
}
在線運行程序[10]
在上面程序的第 13 行,我們將一個切片傳遞給了 modify 函數(shù)。在 modify 函數(shù)中,我們把切片的第一個元素修改為 90。程序也會輸出 [90 90 91]。所以別再傳遞數(shù)組指針了,而是使用切片吧。上面的代碼更加簡潔,也更符合 Go 語言的習慣。
Go 不支持指針運算
Go 并不支持其他語言(例如 C)中的指針運算。
package?main
func?main()?{
????b?:=?[...]int{109,?110,?111}
????p?:=?&b
????p++
}
在線運行程序[11]
上面的程序會拋出編譯錯誤:main.go:6: invalid operation: p++ (non-numeric type *[3]int)。
我在 github[12] 上創(chuàng)建了一個程序,涵蓋了所有我們討論過的內容。
關于指針的介紹到此結束。祝您愉快。
下一教程 - 結構體[13]
via: https://golangbot.com/pointers/
作者:Nick Coghlan[14]譯者:Noluye[15]校對:polaris1119[16]
本文由 GCTT[17] 原創(chuàng)編譯,Go 中文網[18] 榮譽推出
參考資料
在線運行程序: https://play.golang.org/p/A4vmlgxAy8
[2]在線運行程序: https://play.golang.org/p/yAeGhzgQE1
[3]在線運行程序: https://play.golang.org/p/m5pNbgFwbM
[4]在線運行程序: https://play.golang.org/p/cdmvlpBNmb
[5]在線運行程序: https://play.golang.org/p/3n2nHRJJqn
[6]在線運行程序: https://play.golang.org/p/lOIznCbcvs
[7]在線運行程序: https://play.golang.org/p/k7YR0EUE1G
[8]切片: https://golangbot.com/arrays-and-slices/
[9]切片: https://golangbot.com/arrays-and-slices/
[10]在線運行程序: https://play.golang.org/p/rRvbvuI67W
[11]在線運行程序: https://play.golang.org/p/WRaj4pkqRD
[12]github: https://github.com/golangbot/pointers
[13]結構體: https://studygolang.com/articles/12263
[14]Nick Coghlan: https://golangbot.com/about/
[15]Noluye: https://github.com/Noluye
[16]polaris1119: https://github.com/polaris1119
[17]GCTT: https://github.com/studygolang/GCTT
[18]Go 中文網: https://studygolang.com/
推薦閱讀
