1. 程式人生 > >Golang教程15節--指標

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 上建立了一個程式,涵蓋了所有我們討論過的內容。

關於指標的介紹到此結束。祝您愉快。