切片的指针和循环

时间:2018-07-12 13:42:49

标签: loops pointers go slice

我认为这个问题曾问过几次,但我仍然感到困惑:

我有以下代码:

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变量的行为?您能否详细地画出或解释一下,通过两个循环在迭代过程中内存中会发生什么?

2 个答案:

答案 0 :(得分:4)

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

那么范围是将集合中的每个项目都提取并复制到您调用range 时创建的存储位置。

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

当您执行&cmd时,您正在使用指针。该指针指向将每个切片项目复制到的共享内存位置。

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

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

这就是为什么您得到输出的原因

update
update
update

答案 1 :(得分:1)

这是因为在一种情况下,您将地址传递给struct,而在另一种情况下,您将传递字符串值。因此,每当将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)
    }
}

&cmd将指向相同的地址,该地址将在每次迭代中将其值更新到相同的位置,而不是在cmd中不将地址传递给loop1的情况。 / p>

已编辑 这是[]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