为什么golang行为中的字符串指针在范围循环中违反直觉?

时间:2019-07-09 03:04:56

标签: pointers go

使用以下代码:https://play.golang.org/p/tCm1W-K-6ob

此代码将打印:[c c c],但[a b c]除外。

type A struct {
    a *string
}

func f() {
    slist := []string{"a", "b", "c"}
    list := make([]*A, len(slist))
    for i, v := range slist {
        item := &A{
            a: &v,
        }
        list[i] = item
    }
    fmt.Printf("[%s %s %s]", *list[0].a, *list[1].a, *list[2].a)
}

func main() {
    f()
}

为什么列表不是["a", "b", "c"]?范围或&string发生了什么?

4 个答案:

答案 0 :(得分:1)

https://play.golang.org/p/utJrUmxeqfh

size
package main

import (
    "fmt"
)

func main() {
    foo := []int{1, 2, 3}
    for _, f := range foo {
        fmt.Printf("value is %d, addr is %p \n", f, &f)
    }
    fmt.Println("Hello, playground")
}

value is 1, addr is 0x414020 value is 2, addr is 0x414020 value is 3, addr is 0x414020 Hello, playground 中的值f具有相同的地址

答案 1 :(得分:1)

所有项都包含一个局部变量v的地址。

如果您的目标是在a字段中分配slice元素的地址,请执行以下操作:

for i := range slist {
    item := &A{
        a: &slist[i],  // address of element instead of local variable.
    }
    list[i] = item
}

Run it on the Go Playground

您还可以通过在每次循环迭代中创建一个新变量来获得所需的输出:

for i, v := range slist {
    v := v  // create new value on each iteration.
    item := &A{
        a: &v,
    }
    list[i] = item
}

Run it on the Go Playground

答案 2 :(得分:0)

问题是迭代器变量仅分配一次,并且它指向的值在循环的每次迭代中都会更改:

package main

import (
    "fmt"
)

type A struct {
    a *string
}

func f() {
    slist := []string{"a", "b", "c"}
    list := make([]*A, len(slist))

    // v is only allocated once and the value at it's address is changed
    // on each iteration.
    for i, v := range slist {
        fmt.Printf("Iteration %d: address of v %#v, value of v \"%s\"\n", i+1, &v, v)
        // Add in a temp variable, which will get allocated
        // and hence have a new address each iteration.
        t := v

        // Now assign that temp variable's address to the Item's a.
        list[i] = &A{
            a: &t,
        }
    }
    fmt.Printf("[%s %s %s]", *list[0].a, *list[1].a, *list[2].a)
}

func main() {
    f()
}

Run on playground

但是,我强烈感觉这是不直观的,因为我们几乎都在谈论过早的优化。 (稍微简化了一下):Go中的字符串基本上是一个字节的切片([]byte),其中一个切片已经是指向后备数组的指针。有关详细信息,请参见Go Slices: usage and internals

答案 3 :(得分:-2)

您可以尝试

在这里,我可以运行您的代码并进行一些更改。

https://play.golang.org/p/GmU3goeOKdF

这是代码

package main

import (
    "fmt"
)

type A struct {
    a  *string
}

func f() {
    slist := []string{"a", "b", "c"}
    list := make([]*A, len(slist))
    for i, v := range slist {
        item := &A{
            a: &v,
        }
        list[i] = item

    fmt.Printf("%s", *list[i].a)
    }
}
func main() {
    f()
}

输出 abc