Go语言遍历结构体切片时指针赋值值覆盖问题问询
嘿,这个问题我太熟了——这是Go语言range循环里一个超级容易踩的经典陷阱!
问题根源
在你的循环代码里:
for _, d := range entries { // ... 赋值指针的操作 }
变量d并不是每次循环都新建的!它在整个循环周期里只会被初始化一次,每次迭代只是把切片当前元素的值拷贝到d这个已经存在的变量里。换句话说,所有循环步骤里的d都是同一个内存地址。
当你执行t1 = &d.LogTime时,你拿到的其实是d这个复用变量里LogTime字段的地址。等循环跑完,d里装的是切片最后一个元素的值,那t1和t2自然都指向最后那个元素的LogTime了,这就导致你看到两个变量值一样的情况。
你找到的解决方案为啥有效?
你改成tmp := d.LogTime; t1 = &tmp是完全正确的!因为tmp是在每次循环迭代内部创建的新变量,每个tmp都有自己独立的内存空间,把它的地址赋值给t1,就不会被后续循环的d覆盖了。
还有其他两种常用方案
直接用切片索引取原元素指针
这种方式不需要拷贝值,直接操作切片里原元素的指针,效率更高:for i := range entries { d := &entries[i] // 直接取切片中第i个元素的指针 if d.EntryType == print { t1 = &d.LogTime } if d.EntryType == saw { t2 = &d.LogTime } }存储值而非指针(如果业务允许)
既然time.Time是值类型,如果你不需要保留指针的话,直接存值更简单,彻底避开指针问题:var t1, t2 time.Time // 改成值类型变量 for _, d := range entries { if d.EntryType == print { t1 = d.LogTime } if d.EntryType == saw { t2 = d.LogTime } }
最后划个重点
以后用Go的range循环时,只要涉及到取迭代变量的地址,一定要警惕变量复用这个特性。要么用临时变量开辟新空间,要么直接操作切片索引的元素指针,或者干脆存值不存指针,就能避开这个坑啦!
内容的提问来源于stack exchange,提问作者Thomas Geulen
相关产品推荐
相关产品推荐

