Golang教程15節--指標
什麼是指標?
指標是一種儲存變數記憶體地址的變數。
如上圖所示,變數 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) }
& 操作符用於獲取變數的地址。上面程式的第 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) } }
上面的程式中,b
初始化為 nil
,接著將 a
的地址賦值給 b
。程式會輸出:
b is <nil>
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) }
在上面程式的第 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,由於 a
指向了 b
,因此 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 行,我們向函式 change
傳遞了指標變數 b
,而 b
儲存了 a
的地址。程式的第 8 行在 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 行中,我們將陣列的地址傳遞給了 modify
函式。在第 8 行,我們在 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)
}
該程式也會輸出 [90 90 91]
。
這種方式向函式傳遞一個數組指標引數,並在函式內修改陣列。儘管它是有效的,但卻不是 Go 語言慣用的實現方式。我們最好使用切片來處理。
接下來我們用切片來重寫之前的程式碼。
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 語言的習慣。
Go 不支援指標運算
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 上建立了一個程式,涵蓋了所有我們討論過的內容。
關於指標的介紹到此結束。祝您愉快。