Go 区分指针引用和值引用,使用结构体时,明确指针引用和值引用的区别很重要。
前面说过,数据值存储在计算机内存中。指针包含值的内存地址,这意味着使用指针可读写存储的值。创建结构体实例时,给数据字段分配内存并给它们指定默认值;然后返回指向内存的指针,并将其赋给一个变量。使用简短变量赋值时,将分配内存并指定默认值。
a := Drink{}
复制结构体时,明确内存方面的差别很重要。将指向结构体的变量赋给另一个变量时,被称为赋值。
a := b
赋值后,a与b相同,但它是b的副本,而不是指向b的引用。修改b不会影响a,反之亦然。如下程序演示了这种行为。
package main
import (
"fmt"
)
type Drink struct {
Name string
Ice bool
}
func main() {
a := Drink{
Name: "Lemonade",
Ice: true,
}
b := a
b.Ice = false
fmt.Printf("%+v\n", b)
fmt.Printf("%+v\n", a)
fmt.Printf("%p\n", &a)
fmt.Printf("%p\n", &b)
}
运行结果如下:
解读如下:
- 声明结构体类型Drink。
- 创建结构体Drink的一个实例,并将其赋给变量a。
- 声明变量b并将a赋给它。
- 修改b的数据字段Ice。
- 将b的值打印到终端。
- 将a的值打印到终端,以证明修改b不会影响a。
- 使用fmt.Printf将a和b的内存地址打印到终端,以证明它们的内存地址不同。
要修改原始结构体实例包含的值,必须使用指针。指针是指向内存地址的引用,因此使用它操作的不是结构体的副本而是其本身。要获得指针,可在变量名前加上和号。可对程序清单7.9进行修改,以使用指针引用而不是值引用,如程序所示。
package main
import (
"fmt"
)
type Drink struct {
Name string
Ice bool
}
func main() {
a := Drink{
Name: "Lemonade",
Ice: true,
}
b := &a
b.Ice = false
fmt.Printf("%+v\n", *b)
fmt.Printf("%+v\n", a)
fmt.Printf("%p\n", b)
fmt.Printf("%p\n", &a)
}
运行结果如下:
相比于第一个程序,上面程序的不同之处如下。
- 将指向a的指针(而不是a本身)赋给b,这是使用和号字符表示的。
- 修改b时,将修改分配给a的内存,因为a和b指向相同的内存。
- 打印a和b的值时,将发现它们的值相同。请注意,由于b是指针,因此必须使用星号字符对其进行引用。
- 将b和a的内存地址打印到控制台,以证明它们相同。
指针和值的差别很微妙,但选择使用指针还是值很容易区分:如果需要修改原始结构体实例,就使用指针;如果要操作一个结构体,但不想修改原始结构体实例,就使用值。
酷客教程相关文章:
评论前必须登录!
注册