Go 区分指针引用和值引用

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

运行结果如下:
Go 区分指针引用和值引用

解读如下:

  • 声明结构体类型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)
 }

运行结果如下:
Go 区分指针引用和值引用

相比于第一个程序,上面程序的不同之处如下。

  • 将指向a的指针(而不是a本身)赋给b,这是使用和号字符表示的。
  • 修改b时,将修改分配给a的内存,因为a和b指向相同的内存。
  • 打印a和b的值时,将发现它们的值相同。请注意,由于b是指针,因此必须使用星号字符对其进行引用。
  • 将b和a的内存地址打印到控制台,以证明它们相同。

指针和值的差别很微妙,但选择使用指针还是值很容易区分:如果需要修改原始结构体实例,就使用指针;如果要操作一个结构体,但不想修改原始结构体实例,就使用值。

酷客教程相关文章:

赞(0)

评论 抢沙发

评论前必须登录!