我有这个简单的代码:
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/