V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
superfatboy

初学 golang,小白求解惑!

  •  1
     
  •   superfatboy · Nov 25, 2021 · 4587 views
    This topic created in 1617 days ago, the information mentioned may be changed or developed.
    package main
    
    import "fmt"
    
    type Test struct {
    	Id   int64
    	Name string
    }
    
    func main() {
    	var list = []Test{
    		Test{Id: 1, Name: "1XX"},
    		Test{Id: 2, Name: "2XX"},
    		Test{Id: 3, Name: "3XX"},
    	}
    	for _, v := range list {
    		v.Name = "888"
    	}
            fmt.Println(list)
    }
    

    为啥修改 Name 貌似没生效

    Supplement 1  ·  Nov 25, 2021
    习惯了写 js ,学习 go 有点不适应啊!!
    27 replies    2021-11-26 18:11:17 +08:00
    rrfeng
        1
    rrfeng  
       Nov 25, 2021 via Android   ❤️ 2
    for range 是 copy 一份对象,所以没改原始的。
    用索引访问数组元素就可以了。
    yin1999
        2
    yin1999  
       Nov 25, 2021 via Android   ❤️ 1
    因为你获取的是一个拷贝,而不是 slice 中的原始对象
    helone
        3
    helone  
       Nov 25, 2021   ❤️ 1
    ```
    for k, _ := range list {
    list[k].Name = "888"
    }
    ```
    这样就行了
    superfatboy
        4
    superfatboy  
    OP
       Nov 25, 2021
    @rrfeng
    @yin1999
    感谢,刚才仔细看了一下教程,果然是一个拷贝,nnd ,大意了
    superfatboy
        5
    superfatboy  
    OP
       Nov 25, 2021
    @helone 感谢
    corningsun
        6
    corningsun  
       Nov 25, 2021
    Javaer 震怒 ~
    shakaraka
        7
    shakaraka  
    PRO
       Nov 25, 2021   ❤️ 1
    ```
    func main() {
    var list = []*Test{
    {Id: 1, Name: "1XX"},
    {Id: 2, Name: "2XX"},
    {Id: 3, Name: "3XX"},
    }
    for _, v := range list {
    v.Name = "888"
    }

    for _, v := range list {
    fmt.Printf("%+v\n", v)
    }
    }
    ```

    用指针就好了
    Rooger
        8
    Rooger  
       Nov 25, 2021   ❤️ 1
    原因:for range 的方式其实一种 copy 了 list 。
    两种修改方式,一种就是 `list[k].Name = 888`,另一种是将 slice 修改为存储指针 var list = []*Test 。
    同样的坑你也应该注意,不能使用 k 和 v 的地址。
    whyso
        9
    whyso  
       Nov 25, 2021
    @corningsun 为啥
    superfatboy
        10
    superfatboy  
    OP
       Nov 25, 2021
    @wunonglin 这个方法不错
    ClarkAbe
        11
    ClarkAbe  
       Nov 25, 2021 via Android
    用指针,和 C 系一样
    ClarkAbe
        12
    ClarkAbe  
       Nov 25, 2021 via Android
    不过 js 那边不是有句名言不建议在循环里面修改元素嘛
    corningsun
        13
    corningsun  
       Nov 25, 2021
    @whyso

    Java 和 Go 的 for 循环 默认策略完全相反。
    Java 必须要主动拷贝才会新建对象。这种 List 结构还得深拷贝,不然用的还是同一个对象。
    Go 和 Java stream() 比较像了。
    superfatboy
        14
    superfatboy  
    OP
       Nov 25, 2021
    @ClarkAbe 哈哈,怎么顺手怎么来,不过确实不推荐
    yrj
        15
    yrj  
       Nov 25, 2021 via iPad
    你这么理解,range 的时候,其实是 copy 了一份新的数据给 for 所以你在 for 里修改的是 copy 的新数据。可以按照楼上指针的写法,原理是直接操作了指针
    mmuggle
        16
    mmuggle  
       Nov 26, 2021
    虽然 for range 确实是循环的副本,但是这个是循环的切片,切片结构是指向一个底层数组的指针

    所以我觉得原因是 v 是在 for 循环的时候初始化的,循环体中修改的是 v 的 Name ,而不是切片元素的 Name
    SimbaPeng
        17
    SimbaPeng  
       Nov 26, 2021   ❤️ 10
    我真的怀疑楼上的回复者,真的会 Golang ?

    什么 range copy 了 list ,range 策略,深拷贝浅拷贝?能不能不要在这里误人子弟?

    这里的关键就是 v := range list 是个赋值操作,将 list 元素赋值给 v ,golang 里只有值传递,所以 list 元素在复制给 v 的时候必然产生了拷贝。而结构体是值类型,没有引用的对象,所以是拷贝整个结构体,直接修改 v 的属性就是修改拷贝的结构体,所以原结构体属性并不会改变。

    同理,但凡你将一个结构体赋值给一个新变量,然后修改新变量的字段,都不会对原结构体有任何影响。

    这跟你用没用 range 一点关系都没有。

    如果你的 list 里存的是结构体的指针,range 的时候一样会产生拷贝,不过拷贝的是指针结构,底层引用的结构体不会产生拷贝,所以你解指针修改字段的时候,修改的是同一个结构体。但如果将一个新的指针赋值给 v ,原 list 的元素依然不会被改变。

    最后总结一句话,golang 只有值传递。
    SimbaPeng
        18
    SimbaPeng  
       Nov 26, 2021
    ```
    func main() {
    a := []int{1, 2, 3}
    for i, v := range a {
    if i == 0 {
    a[2] = 4
    }
    fmt.Println(v)
    }
    }
    ```

    那些说是因为 range 的是 list 的副本的,自己运行一下这段代码。copy 的是切片,又不是底层数组。
    ila
        19
    ila  
       Nov 26, 2021 via Android
    range map 时可以修改 map 的 value ,倒推过来看看
    cassyfar
        20
    cassyfar  
       Nov 26, 2021
    @SimbaPeng 这是正解

    所以如果你要用 for loop 改值,换成指针就好了。实际操作中,也基本用的指针数组存放 struct 。
    mmuggle
        21
    mmuggle  
       Nov 26, 2021   ❤️ 1
    代码其实相当于下面这种
    ```
    package main

    import "fmt"

    type Test struct {
    Id int64
    Name string
    }

    func main() {
    var list = []Test{
    Test{Id: 1, Name: "1XX"},
    Test{Id: 2, Name: "2XX"},
    Test{Id: 3, Name: "3XX"},
    }
    v := Test{}
    for _, v = range list {
    v.Name = "888"
    }
    fmt.Println(list)
    }
    ```
    superfatboy
        22
    superfatboy  
    OP
       Nov 26, 2021
    @mmuggle 你这种写法就容易理解了
    Mitt
        23
    Mitt  
       Nov 26, 2021
    @SimbaPeng #17 一个意思的两种表达而已,虽然你说的是对的,但你显然误解了上面所说的拷贝,在我看来两种说法都没有问题
    whyso
        24
    whyso  
       Nov 26, 2021
    @SimbaPeng 真相了,LZ 的问题在于 V 而不是 range ,循环修改值我一般用 for i:=0; i<len();i++ {}这种,不修改用 range
    SimbaPeng
        25
    SimbaPeng  
       Nov 26, 2021   ❤️ 1
    @Mitt 在我看来你所谓的“两种表达”,描述的本质是不同,楼上有些回复的本意是:range 会 产生迭代对象的副本,所以无法修改原对象。事实是 range 确实会产生 迭代对象的副本,但这不是 LZ 不能用 v 来修改原对象中元素的原因。

    有的甚至扯到深浅拷贝,认为 range 产生了深拷贝,但其实 range 只会 产生 切片结构的拷贝,底层引用的数组还是同一个。
    oluoluo
        26
    oluoluo  
       Nov 26, 2021   ❤️ 1
    ```
    package main

    import "fmt"

    type Item struct {
    Id uint64
    Name string
    }

    func main() {
    list := []Item{
    {Id: 1, Name: "1xxx"},
    {Id: 2, Name: "2xxx"},
    {Id: 3, Name: "3xxx"},
    }
    for i := 0; i < len(list); i++ {
    fmt.Printf("item %d address: %p\n", i, &list[i])
    }
    for _, v := range list {
    v.Name = "888"
    fmt.Printf("list item address: %p\n", &v)
    }
    fmt.Println("---------------")
    fmt.Println(list)
    }
    ```
    item 0 address: 0x14000060050
    item 1 address: 0x14000060068
    item 2 address: 0x14000060080
    list item address: 0x1400000c030
    list item address: 0x1400000c030
    list item address: 0x1400000c030
    ---------------
    [{1 1xxx} {2 2xxx} {3 3xxx}]
    wangritian
        27
    wangritian  
       Nov 26, 2021
    想修改内容时,我都会这样写:
    for i := range list {
    v := &(list[i])
    v.Name = "xxx"
    }
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2457 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 59ms · UTC 04:13 · PVG 12:13 · LAX 21:13 · JFK 00:13
    ♥ Do have faith in what you're doing.