02-15 指針


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)
}

& 操作符用於獲取變量的地址。上面程序的第 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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM