Go指针复制问题

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
}

这段代码输出为什么是这个?希望麻烦大家帮忙解答下。https://github.com/dockerq/go-pointer-trick

0th r, id: 0, cpu: 4.000000, mem: 16000.000000
1th r, id: 1, cpu: 2.000000, mem: 8000.000000
2th r, id: 2, cpu: 1.000000, mem: 4000.000000
[0xc42000e2a0 0xc42000e2a0 0xc42000e2a0]
id: 2, cpu: 1.000000, mem: 4000.000000
id: 2, cpu: 1.000000, mem: 4000.000000
id: 2, cpu: 1.000000, mem: 4000.000000
已邀请:
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)
}

这里的 r 一直是同一个地址的值的,for 循环的每次是覆盖旧的 r,你要用 *rr[k]

xkey - go

赞同来自: adolphlwq

刚刚评论里的代码经过markdown处理有问题,重新复制代码,放到前一个评论的回复里了

xkey - go

赞同来自:

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 }

adolphlwq - github.com/adolphlwq

赞同来自:

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)
}

关键在for...range,for循环中的k,r是临时变量,它们在确定之后地址不会改变,但是值会改变。rs = append(rs, &r)这里&r取r的地址一直是同一个值,循环结束后r的值是最后一次循环的值。

参考:聊聊Go中的Range关键字

lys86_1205

赞同来自:

package main

import "fmt"

func main(){

    r := []int{1, 2, 3, 4, 5}
    var v int
    fmt.Printf("v: %p\n", &v)
    for k, v := range r {
        fmt.Printf("%p--%p\n", &k, &v)
    }
}

测试发现在for循环中,k, v并没有创建新的变量。

测试结果:

novacaine

赞同来自:

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这个方法的实现,简直无法理解为什么要这么写。

要回复问题请先登录注册