【问题标题】:Slice of pointers and loop [duplicate]切片指针和循环[重复]
【发布时间】:2018-12-20 18:43:23
【问题描述】:

我认为这个问题被问了好几次,但我仍然感到困惑:

我有以下代码:

type obj struct {
    s *string
}

var cmdsP = []*string {
    stringPointer("create"),
    stringPointer("delete"),
    stringPointer("update"),
}

var cmds = []string {
    "create",
    "delete",
    "update",
}

// []*string
func loop1() {
    slice := make([]obj, 0, 0)

    for _, cmd := range cmdsP {
        slice = append(slice, obj{cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
}

// []string
func loop2() {
    slice := make([]obj, 0, 0)
    for _, cmd := range cmds {
        slice = append(slice, obj{&cmd})
    }
    for _, o := range slice {
        fmt.Println(*o.s)
    }
}

func stringPointer(v string) *string {
    return &v
}

https://play.golang.org/p/65Le_8Pi3Mi

唯一的区别在于切片语义[]*string[]string 它如何改变cmd 变量的行为?您能否详细说明或详细说明在通过两个循环进行迭代期间内存中发生的情况?

【问题讨论】:

    标签: loops pointers go slice


    【解决方案1】:

    当你在一个集合上调用 range 时,go 运行时会初始化 2 个内存位置;一个用于索引(在本例中为_),一个用于值cmd

    然后 range 的作用是获取集合中的每个项目,并将它们复制到它在您调用 range 时创建的内存位置。

    这意味着切片中的每个项目都会被一个一个地放入该内存位置。

    当您执行&cmd 时,您将获得一个指针。该指针指向每个切片项被复制到的共享内存位置。

    所有使用&cmd 创建的指针都指向同一个内存位置。

    这意味着在range 完成后,指针指向的内存位置中剩下的唯一值是range 迭代中的最后一个值。

    这就是你得到输出的原因

    update
    update
    update
    

    【讨论】:

      【解决方案2】:

      这是因为在一种情况下,您将地址传递给 struct,而在另一种情况下,您正在传递字符串值。因此,每当您将结构字段附加到切片时,它将更新同一地址上的现有值。这就是为什么你只获得指针类型切片的最后一个值,在这种情况下是update

      // []string
      func loop2() {
          slice := make([]obj, 0, 0)
          for _, cmd := range cmds {
              slice = append(slice, obj{&cmd})
          }
          for _, o := range slice {
              fmt.Println(*o.s)
          }
      }
      

      已编辑 这是[]obj 的一个切片,它有一个指针类型的字段 *string

      slice := make([]obj, 0, 0)
      

      因此,当您循环访问它时,实际上是在传递cmd 的地址并将其分配给指针字段。

      要查看差异,请打印类型,您将获得以下信息:

      func loop1() {
          slice := make([]string, 0, 0)
      
          for _, cmd := range cmdsP {
              slice = append(slice, *cmd)
          }
          for _, o := range slice {
              fmt.Println(o)
          }
      
          fmt.Printf("%#v\n", slice)
      }
      

      Playground example

      在上述情况下,您正在创建一个字符串切片。

      // []*string
      func loop1() {
          slice := make([]obj, 0, 0)
      
          for _, cmd := range cmdsP {
              slice = append(slice, obj{cmd})
          }
          for _, o := range slice {
              fmt.Println(*o.s)
          }
          fmt.Printf("%#v\n", slice)
      }
      

      而在第二种情况下,您正在创建 obj 结构的切片,其中包含指向 string 的指针字段。他们都是不同的情况。使用fmt 包查看您在loop1 函数中创建的切片的实际类型

      Playground example

      【讨论】:

      • 感谢您的回答!你说So whenever you append an struct field to a slice 但如果我附加字符串字段slice := make([]string, 0, 0) 一切都很好。有什么区别? play.golang.org/p/7UdLlP2GiLw
      • @Rudziankoŭ 请检查编辑后的答案
      猜你喜欢
      • 2017-01-25
      • 2020-03-26
      • 2015-05-01
      • 2021-12-15
      • 1970-01-01
      • 2016-07-17
      • 2018-10-07
      • 2016-07-03
      • 1970-01-01
      相关资源
      最近更新 更多