golang指针
什么是指针,指针是存储一个变量的内存地址的变量。在下图,变量 b 的值是 156,存储在地址为 0xc0000100a8的内存中。变量 a 存储了变量 b 的地址。现在可以说 a 指向 b。
一、指针的声明
指向类型 T 的指针用 *T 表示。先看一个测试代碱:
1package main
2import (
3 "fmt"
4)
5func main() {
6 b := 255
7 var a *int = &b
8 fmt.Printf("Type of a is %T\n", a)
9 fmt.Println("address of b is", a)
10}
& 操作符用来获取一个变量的地址。在上面的程序中,第 9 行(var定义所在行)我们将 b 的地址赋给 a(a 的类型为 *int)。现在我们说 a 指向了 b。当我们打印 a 的值时,b 的地址将会被打印出来。程序的输出为:
1Type of a is *int
2address of b is 0xc0000100a8
你可能得到的是一个不同的 b 的地址,因为 b 可以在内存中的任何地方。
二、指针的空值与计算
指针的空值为 nil ,其一般用于进行判断对比操作。代码如下:
1package main
2import (
3 "fmt"
4)
5func main() {
6 a := 25
7 var b *int
8 if b == nil {
9 fmt.Println("b is", b)
10 b = &a
11 fmt.Println("b after initialization is", b)
12 }
13}
上面的代码执行后,结果如下:
1E:\golang\point>go run 02.go
2b is <nil>
3b after initialization is 0xc0000100a8
该程序中,b 的初始值为 nil。接着将 a 的地址赋值给 b。
Go 中的指针只能进行比较运算,不支持其他语言(比如C)中的指针运算,如下代码:
1package main
2func main() {
3 b := [...]int{109, 110, 111}
4 p := &b
5 p++
6}
上面的程序将报错:main.go:6: invalid operation: p++ (non-numeric type *[3]int) 。
三、指针的解引用
解引用指针的意思是通过指针访问被指向的值。指针 a 的解引用表示为:*a。先看下面的代码:
1package main
2import (
3 "fmt"
4)
5func main() {
6 b := 255
7 a := &b
8 fmt.Println("address of b is", a)
9 fmt.Println("value of b is", *a)
10}
上面程序的第10行,我们将 a 解引用并打印这个解引用得到的值。和我们预期的一样,程序打印的是 b 的值。程序的输出为:
1E:\golang\point>go run 04.go
2address of b is 0xc000062090
3value of b is 255
上面有提到,golang语言不能像C语言一样对指针进行计算操作,不过其可以对其解引用进行计算操作,如下:
1package main
2import (
3 "fmt"
4)
5func main() {
6 b := 255
7 a := &b
8 fmt.Println("address of b is", a)
9 fmt.Println("value of b is", *a)
10 *a++
11 fmt.Println("new value of b is", b)
12}
执行后输出结果如下:
1E:\golang\point>go run 05.go
2address of b is 0xc0000100a8
3value of b is 255
4new value of b is 256
从执行结果可以看出,我们将 a 指向的值自增 1,这样做也改变了 b 的值,因为 a 指向 b。因此 b 的值变为 256。
四、传递指针给函数
下面我们演示的代码是将指向 a 的指针 b 传递给函数 change。在函数 change 内部,通过解引用修改了 a 的值,代码如下:
1package main
2import (
3 "fmt"
4)
5func change(val *int) {
6 *val = 55
7}
8func main() {
9 a := 58
10 fmt.Println("value of a before function call is", a)
11 b := &a
12 change(b)
13 fmt.Println("value of a after function call is", a)
14}
15# 执行结果如下
16E:\golang\point>go run 06.go
17value of a before function call is 58
18value of a after function call is 55
需要注意的是不要传递指向数组的指针给函数,而是使用切片。假设我们需要通过函数修改一个数组。一个办法是将数组的指针作为参数传递给函数。
1package main
2import (
3 "fmt"
4)
5func modify(arr *[3]int) {
6 (*arr)[0] = 90
7}
8func main() {
9 a := [3]int{89, 90, 91}
10 modify(&a)
11 fmt.Println(a)
12}
在上面的程序中,第13行,数组 a 的地址传递给了函数 modify。在第8行的 modify 函数中,我们通过解引用的方式将数组的第一个元素赋值为 90。程序输出为:[90 90 91]。a[x] 是 (*a)[x] 的简写,因此上面的程序中,(*arr)[0] 可以替换为 arr[0]。让我们用这种简写方式重写上面的程序:
1package main
2import (
3 "fmt"
4)
5func modify(arr *[3]int) {
6 arr[0] = 90
7}
8func main() {
9 a := [3]int{89, 90, 91}
10 modify(&a)
11 fmt.Println(a)
12}
程序的输出依然是:[90 90 91] 。
虽然可以通过传递数组指针给函数的方式来修改原始数组的值,但这在 Go 中不是惯用的方式,我们可以使用切片完成同样的事情。让我们用切片的方式重写上面的程序:
1package main
2import (
3 "fmt"
4)
5func modify(sls []int) {
6 sls[0] = 90
7}
8func main() {
9 a := [3]int{89, 90, 91}
10 modify(a[:])
11 fmt.Println(a)
12}
在上面的程序中,第13行,我们传递了一个切片给 modify 函数。在函数内部,切片的第一个元素被修改为 90。程序的输出为:[90 90 91]。所以请不要以数组指针作为参数传递给函数,而是使用切片:)。这样的代码更加简洁,在 Go 中更常被使用。
捐赠本站(Donate)
如您感觉文章有用,可扫码捐赠本站!(If the article useful, you can scan the QR code to donate))
- Author: shisekong
- Link: https://blog.361way.com/golang-point/6341.html
- License: This work is under a 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. Kindly fulfill the requirements of the aforementioned License when adapting or creating a derivative of this work.