我有这个简单的代码:
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}。这里发生了什么?
{0} {1} {2} {3}
{0} {3} {3} {3}
这是因为在for循环中,您使用 副本 而不是slice / array元素本身进行操作。
for
将for ... range使得它遍历元素的副本,并追加此一时,循环变量的地址- 这是在所有的迭代相同。因此,您将相同的指针添加3次。而且此临时变量将Foo{3}在最后一次迭代(数组的最后一个元素)中设置为,因此这就是为什么您看到该变量打印了3次的原因。
for ... range
Foo{3}
修复:不添加循环变量的地址,而是添加数组元素的地址:
for i := range b { a = append(a, &b[i]) }
输出(在Go Playground上尝试):
在Go中,有 指针 类型和 非指针 类型,但是没有“引用”(在C ++和Java中使用的含义)。考虑到Go中没有“引用”类型的事实,这不是意外的行为。循环变量只是一个“普通”局部变量,它只能保存一个值(可以是指针或非指针),而不能保存引用。
从这个答案摘录:
指针是值,就像说int数字一样。区别在于该值的解释:指针被解释为内存地址,而ints被解释为整数。 当要改变类型的变量的值int,则通过一个指向int它的类型的*int,并且修改尖锐的物体:*i = newvalue(分配值是一个int)。 指针也是如此:当您想要更改指针类型的变量的值时*int,可以将指针传递给类型的指针,*int然后**int修改指向的对象:(*i = &newvalue分配的值为*int)。
指针是值,就像说int数字一样。区别在于该值的解释:指针被解释为内存地址,而ints被解释为整数。
int
当要改变类型的变量的值int,则通过一个指向int它的类型的*int,并且修改尖锐的物体:*i = newvalue(分配值是一个int)。
*int
*i = newvalue
指针也是如此:当您想要更改指针类型的变量的值时*int,可以将指针传递给类型的指针,*int然后**int修改指向的对象:(*i = &newvalue分配的值为*int)。
**int
*i = &newvalue
总而言之,循环变量只是具有您要循环的数组/切片的元素类型的普通变量,并且要使其具有实际迭代的值,必须将值赋给该变量,该值将复制该值。在下一次迭代中将覆盖它。