arrays - 奇怪的golang“append ”行为(覆盖 slice 中的值)

标签 arrays for-loop go append slice

我有这个简单的代码:

import "fmt"

type Foo struct {
    val int
}

func main() {
    var a = make([]*Foo, 1)
    a[0] = &Foo{0}

    var b = [3]Foo{Foo{1}, Foo{2}, Foo{3}}
    for _, e := range b {
        a = append(a, &e)
    }

    for _, e := range a {
        fmt.Printf("%v ", *e)
    }
}

我期望它可以打印{0} {1} {2} {3},但是它可以打印{0} {3} {3} {3}。这里发生了什么?

最佳答案

这是因为在for循环中,您使用副本而不是slice / array元素本身进行操作。for ... range复制其循环的元素,然后附加此临时循环变量的地址-在所有迭代中都相同。因此,您将相同的指针添加了3次。并且该临时变量将在上一次迭代(数组的最后一个元素)中设置为Foo{3},因此这就是为什么您看到该变量打印了3次的原因。
修复:不添加循环变量的地址,而是添加数组元素的地址:

for i := range b {
    a = append(a, &b[i])
}
输出(在Go Playground上尝试):
{0} {1} {2} {3} 
查看可能重复的Assigned pointer field becomes <nil>
对此行为进行推理
在Go中,有指针类型和非指针类型,但是没有"references"(在C++和Java中使用它的含义)。考虑到Go中没有“引用”类型的事实,这不是意外的行为。循环变量只是一个“普通”局部变量,它只能保存一个值(可以是指针或非指针),而不能保存引用。
摘自this answer:

指针就是值,就像说int数字一样。区别在于该值的解释:指针被解释为内存地址,而int则被解释为整数。
当您想要更改int类型的变量的值时,可以将指针传递给int类型的*int,然后修改指向的对象:*i = newvalue(分配的值是int)。
指针也是如此:当您要更改指针类型*int的变量的值时,您将指针传递给*int类型的**int,然后修改指向的对象:*i = &newvalue(分配的值是*int)。

总而言之,循环变量只是具有您要循环的数组/ slice 的元素类型的普通变量,并且要使其具有实际迭代的值,必须将值赋给该变量,该值将复制该值。在下一次迭代中将覆盖它。

关于arrays - 奇怪的golang“append ”行为(覆盖 slice 中的值),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64013697/

相关文章:

c++ - 下面的数组和指针示例背后的逻辑是什么?

python - 为什么 numpy 在比较来自两个不同列表的整数和浮点值时返回真实索引

python - 循环创建多个图形后如何保存它们?

c++11 - 为什么 C++11 的范围 for 循环不允许迭代映射?

go - 用 GrantRead 写一个 S3 对象给 400

go - 为什么 tensorflow 的 go/subdirectory 不使用 Bazel rules_go 中的 go_library?

C# 数组不适用于 for 循环

javascript - 如何获取数组中的下几个元素,但在传递最后一个元素时跳回到开头?

javascript - JavaScript 中的 Onmousedown 不起作用 --help!

arrays - 将带有嵌套数组和对象的 JSON 数据解码为 Go 结构