• Go指针复制问题 at 2018年07月17日

    k,v :=range x 本质上是声明两个变量 k ,v (x 为数组或者 slice,k 为下标。x 为 map,k 为 key)。遍历 x 的时候每次将 x 当前元素的值赋值给 v。所以 v 的地址是一直不变的。为了更好的理解。用三种方式改一下你这个例子,使他们输出正确的结果。

    • 第一种 :最不推荐的一种,每次循环将 r 的值赋值给一个新的变量,注意是 t:=r 并不是 t :=&r ,将 r 的值给 t 而不是指针。rs = append(rs, &t) 后。实际上 rs 每次 append 的是一个 r 的值拷贝的地址。
    package main
    
    import "fmt"
    
    type R struct {
        ID  int
        CPU float32
        MEM float32
    }
    
    func main() {
        rr := GetR()
        var rs []*R
        for k, r := range *rr {
            fmt.Printf("%dth r, id: %d, cpu: %f, mem: %f\n", k, r.ID, r.CPU, r.MEM)
            t := r
            rs = append(rs, &t)
        }
        fmt.Println(rs)
        iter(&rs)
    }
    
    func NewR(i int, c, m float32) *R {
        return &R{
            ID:  i,
            CPU: c,
            MEM: m,
        }
    }
    
    func iter(rs *[]*R) {
        for _, r := range *rs {
            fmt.Printf("id: %d, cpu: %f, mem: %f\n", r.ID, r.CPU, r.MEM)
        }
    }
    
    func GetR() *[]R {
        rr := &[]R{
            *NewR(0, 4.0, 16000.0),
            *NewR(1, 2.0, 8000.0),
            *NewR(2, 1.0, 4000.0),
        }
        return rr
    }
    
    • 第二种:不使用 r,直接使用索引赋值,这样总不会取到 r 的地址了吧。
    package main
    
    import "fmt"
    
    type R struct {
        ID  int
        CPU float32
        MEM float32
    }
    
    func main() {
        rr := GetR()
        var rs []*R
        for k, r := range *rr {
            fmt.Printf("%dth r, id: %d, cpu: %f, mem: %f\n", k, r.ID, r.CPU, r.MEM)
            rs = append(rs,  &(*rr)[k])
        }
        fmt.Println(rs)
        iter(&rs)
    }
    
    func NewR(i int, c, m float32) *R {
        return &R{
            ID:  i,
            CPU: c,
            MEM: m,
        }
    }
    
    func iter(rs *[]*R) {
        for _, r := range *rs {
            fmt.Printf("id: %d, cpu: %f, mem: %f\n", r.ID, r.CPU, r.MEM)
        }
    }
    
    func GetR() *[]R {
        rr := &[]R{
            *NewR(0, 4.0, 16000.0),
            *NewR(1, 2.0, 8000.0),
            *NewR(2, 1.0, 4000.0),
        }
        return rr
    }
    
    • 第三种:range 中的元素如果都是指针的话,直接赋值好了。没必要像 2 中那样先转成指针再取值再转成指针。所以修改 GetR 方法,返回指针数组
    package main
    
    import "fmt"
    
    type R struct {
        ID  int
        CPU float32
        MEM float32
    }
    
    func main() {
        rr := GetR()
        var rs []*R
        for k, r := range *rr {
            fmt.Printf("%dth r, id: %d, cpu: %f, mem: %f\n", k, r.ID, r.CPU, r.MEM)
            rs = append(rs,  r)
        }
        fmt.Println(rs)
        iter(&rs)
    }
    
    func NewR(i int, c, m float32) *R {
        return &R{
            ID:  i,
            CPU: c,
            MEM: m,
        }
    }
    
    func iter(rs *[]*R) {
        for _, r := range *rs {
            fmt.Printf("id: %d, cpu: %f, mem: %f\n", r.ID, r.CPU, r.MEM)
        }
    }
    
    func GetR() *[]*R {
        rr := &[]*R{
            NewR(0, 4.0, 16000.0),
            NewR(1, 2.0, 8000.0),
            NewR(2, 1.0, 4000.0),
        }
        return rr
    }
    

    如果你能理解上面那三种方式,我觉得对于指针就有一个比较清晰的认识了。感觉你这里迷惑,问题不是出现在 range 这里。而是对于指针的理解不够深刻。尤其是我看你 func GetR() *[] R 这个方法的实现,简直无法理解为什么要这么写。