下面这段代码输出结果正确吗?
type Foo struct {
bar string
}
func main() {
s1 := []Foo{
{"A"},
{"B"},
{"C"},
}
s2 := make([]*Foo, len(s1))
for i, value := range s1 {
s2[i] = &value
}
fmt.Println(s1[0], s1[1], s1[2])
fmt.Println(s2[0], s2[1], s2[2])
}
输出:
{A} {B} {C}
&{A} &{B} &{C}
1
mark
m
重用就是同个变量重新使用,重新声明就是声明不同的变量吧
加深理解了
di
mark
加深理解了+1
mark
mark
踩坑,打卡
mk
mark
重新声明会开辟新的内存空间,重用的话还是同一块地址,所以已经存到s2的前两个元素会变成最新的v值
mark
for range 用法
马克
for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。
在哪可以看到历史的每日一题呢
value再每次range都会被重用,不是重新开辟一块空间的!故以后最后一次赋值的为基准!
mark
mark
mark
{A} {B} {C} &{C} &{C} &{C} for range 遍历出来的 临时变量 如果是指针,就会出现这种情况。
mark
m
是的
打卡
m
mark
还有一种解决办法,就是有点多余了
range的如果本身就是一个指针切片,如举例的s1变为[]*Foo,会直接用原来的内存地址,会直接把原来的三个地址全部对应赋值过去,相当于进行一次copy操作。这时候对s2[0].bar的值再进行改动就会影响到原值了。
value是一个变量, 使用value的指针,所以s2都会是c
go 1.22 发布之后 答案需要更新一下了, 因为go1.22 for循环语义发生了变化 具体的 1.22之前 输出
1.22 输出
见 : https://go.dev/play/p/1EtkTIWWOfC
mark
mark
m
go 1.22 之前 for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。对value进行取址操作,拿到的都是同一个变量地址 go 1.22 语义发生变化,value都是重新声明的 详见:https://go.dev/play/p/u_hGBXO5KDm
注意在 go1.22 for range 循环已经不是地址复用了,而是重新创建。
mark
参考答案及解析:s2 的输出结果错误。
s2 的输出是 &{C} &{C} &{C},在前面题目我们提到过,for range 使用短变量声明(:=)的形式迭代变量时,变量 i、value 在每次循环体中都会被重用,而不是重新声明。所以 s2 每次填充的都是临时变量 value 的地址,而在最后一次循环中,value 被赋值为{c}。因此,s2 输出的时候显示出了三个 &{c}。
高版本如1.22已修复该问题