[译] part 15: golang 指针 pointers

什么是指针

指针是存储另外一个变量的内存地址的变量。git

在上面的图示中,变量b值为 156 并存储在内存地址 0x1040a124 处。变量a保存了b的地址,那么a就是指针并指向bgithub

声明指针

* 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)
}
复制代码

Run in playgroud函数

运算符用于获取变量的地址。上面程序的第 9 行,咱们将b的地址分配给其类型为* inta。如今能够说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)
}
复制代码

Run in playground

在上述程序的第 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)
}
复制代码

Run in playgroud

上面程序的第 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)
}
复制代码

Run in playgroud

在上面程序的第 14 行,咱们传递指针变量bchange函数。在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)
}
复制代码

Run in playgroud

在上面的程序的第 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)
}
复制代码

Run in playgroud

在上面程序的第 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 不支持指针算数运算

Go 不支持指针算运算,这点和像 C 这样的其余语言不一样。

package main

func main() {  
    b := [...]int{109, 110, 111}
    p := &b
    p++
}
复制代码

Run in playgroud )

上面的程序将抛出编译错误main.go:6: invalid operation: p++ (non-numeric type *[3]int)

我在 github 中建立了一个程序,它涵盖了咱们这一节讨论过的全部内容。

相关文章
相关标签/搜索