- 原文地址:Part 15: Pointers
- 原文做者:Naveen R
- 譯者:咔嘰咔嘰 轉載請註明出處。
指針是存儲另外一個變量的內存地址的變量。git
在上面的圖示中,變量b
值爲 156 並存儲在內存地址 0x1040a124 處。變量a
保存了b
的地址,那麼a
就是指針並指向b
。github
* T
是指針變量的類型,它指向類型爲T
的值。golang
看段代碼吧,數組
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)
}
複製代碼
&
運算符用於獲取變量的地址。上面程序的第 9 行,咱們將b
的地址分配給其類型爲* int
的a
。如今能夠說a
指向b
。當咱們打印a``的值時就是
b`的地址。輸出,this
Type of a is *int
address of b is 0x1040a124
複製代碼
你可能會得到不一樣的地址,由於b
能夠在內存中的任何位置。spa
指針的零值是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)
}
}
複製代碼
Run in playgroudcode
b
在上述程序中最初爲nil
,而後將a
的地址賦值給b
。輸出,cdn
b is <nil>
b after initialisation is 0x1040a124
複製代碼
解引用指針意味着訪問指針指向的變量的值。* 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)
}
複製代碼
在上述程序的第 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)
}
複製代碼
上面程序的第 12 行,咱們將a
指向的值增長 1,它將改變b
的值,由於a
指向b
。所以,b
的值變爲 256。程序的輸出是
address of b is 0x1040a124
value of b is 255
new value of b is 256
複製代碼
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)
}
複製代碼
在上面程序的第 14 行,咱們傳遞指針變量b
給change
函數。在change
函數內部,使用解引用來修改a
的值。此程序輸出,
value of a before function call is 58
value of a after function call is 55
複製代碼
咱們假設想在函數內部對數組進行一些修改,而且數組所作的修改應該對調用者可見。一種方法是將指向數組的指針做爲函數的參數傳遞。
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)
}
複製代碼
在上面的程序的第 13 行,咱們將數組a
的地址傳遞給modify
函數。在modify
函數中,咱們解除引用arr
並將 90 分配給數組的第一個元素。該程序輸出[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)
}
複製代碼
Run in playgroud 該程序也會輸出[90 90 91]
雖然這種將指向數組的指針做爲函數的參數傳遞並對其進行修改的方式有效,但這並非 Go 中的慣用方法。咱們有切片slice )。
咱們使用切片從新上述代碼,
package main
import (
"fmt"
)
func modify(sls []int) {
sls[0] = 90
}
func main() {
a := [3]int{89, 90, 91}
modify(a[:])
fmt.Println(a)
}
複製代碼
在上面程序的第 13 行中,咱們將一個切片傳遞給modify
函數。切片的第一個元素在modify
函數內被修改成 90。該程序也輸出[90 90 91]
。因此忘記將指針傳遞給數組吧,使用切片更乾淨,是慣用的 Go :)。 譯者注:But even this style isn't idiomatic Go. Use slices instead.
這句話是 Go 官方文檔推薦的,其實重要的一點是,在 Go 中數組是定長的,因此看到按引用傳遞的定義func modify(arr *[3]int)
是這個樣子。若是個人數組要擴容還得修改入參,很是不靈活。固然還有其餘的弊病,若是沒有肯定數組能幹什麼,那就按官方文檔的建議來吧。
Go 不支持指針算運算,這點和像 C 這樣的其餘語言不一樣。
package main
func main() {
b := [...]int{109, 110, 111}
p := &b
p++
}
複製代碼
上面的程序將拋出編譯錯誤main.go:6: invalid operation: p++ (non-numeric type *[3]int)
我在 github 中建立了一個程序,它涵蓋了咱們這一節討論過的全部內容。