The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
wewin

请教 golang slice 相关的问题

  •  
  •   wewin · Jul 9, 2019 · 4767 views
    This topic created in 2524 days ago, the information mentioned may be changed or developed.

    我们知道 golang 中 slice 是引用类型,我声明长度为 10 的 slice,往其中插入了 6 个元素。打印能看到 addres4、addres5 地址相同,但是在 main 中打印 slice 的值和在 someChage 方法中打印的结果竟然不一样,请问大佬,这是什么原因?

    代码:

    package main
    
    import "fmt"
    
    func someChage(slice []int) {
    	fmt.Printf("addres2: %p\n", slice)
    
    	slice = append(slice, 1, 2, 3)
    	fmt.Printf("addres3: %p\n", slice)
    
    	slice = append(slice, 4, 5, 6)
    	fmt.Printf("addres4: %p\n", slice)
    	fmt.Println(slice)
    }
    
    func main() {
    	slice := make([]int, 0, 20)
    	fmt.Printf("addres1: %p\n", slice)
    	someChage(slice)
    
    	fmt.Printf("addres5: %p\n", slice)
    	fmt.Println(slice)
    }
    

    结果:

    addres1: 0xc00007a000
    addres2: 0xc00007a000
    addres3: 0xc00007a000
    addres4: 0xc00007a000
    [1 2 3 4 5 6]
    addres5: 0xc00007a000
    []
    
    25 replies    2019-07-09 22:43:28 +08:00
    wewin
        1
    wewin  
    OP
       Jul 9, 2019
    期待大佬的回复
    misaka19000
        2
    misaka19000  
       Jul 9, 2019 via Android   ❤️ 1
    GGGG430
        3
    GGGG430  
       Jul 9, 2019
    https://stackoverflow.com/questions/22811138/print-the-address-of-slice-in-golang

    fmt.Printf("address of slice %p add of Arr %p \n", &slice, &intarr)
    %p will print the address.
    kidlj
        4
    kidlj  
       Jul 9, 2019
    Slice 并不是一个 pure 引用类型,更像是一个聚合类型:

    type IntSlice struct {
    ptr *int
    len int
    cap int
    }

    所以当你在 main 里将 slice 传给 someChange 函数,someChange 获得了一个 slice 的复制( Go 是参数传值),因此 someChange 里的 slice 和 main 函数里的 slice 不相关了。

    两个函数里打印 slice 是相同的地址可能是因为打印的是 slice 底层 underlying array 的地址,两个 slice 指向的是同一个 underlying array,在 append 的过程中并没有改变(因为 cap 还够)。
    petelin
        5
    petelin  
       Jul 9, 2019 via iPhone
    Somechange 函数接受到的是 slice 对象的一个拷贝
    Len cap 都是单独的 不过 底层数组确实是一个
    在执行修改之后 main 的 slice len 还是 0 只不过底层数组多了 1-2-3-4 这个细节对于 slice 是隐藏的 所以看起来没有产生任何修改
    fcten
        6
    fcten  
       Jul 9, 2019
    slice 的地址相同并不意味着它们是同一个 slice。
    你在 someChage 里操作的 slice 和 main 中的 slice 指向同一块数据,所以它们的地址相同。slice 本身是值传递的,所以在 someChage 里使用 append 操作 slice 并不会影响到 main 中的 slice。main 中的 slice 长度一直为 0,所以输出为 []。
    ghonfir
        7
    ghonfir  
       Jul 9, 2019
    golang 都是值复制 没有引用
    petelin
        8
    petelin  
       Jul 9, 2019
    理解一下这段代码, 尤其是 s1[0] 会 panic.
    func dos(s2 []int) {
    s2 = append(s2, 1)
    fmt.Printf("first element add: %p, slice addr: %p, real slice addr: %p", &s2[0], s2, &s2)
    }
    func TestSlice(t *testing.T) {
    s1 := make([]int, 0, 20)
    dos(s1)
    fmt.Println(s1)
    fmt.Println(s1[0])
    }
    tiedan
        9
    tiedan  
       Jul 9, 2019
    楼主,改成这样,你运行一下应该就明白了吧

    package main

    import "fmt"

    func someChage(slice []int) {
    slice[0] = 100
    fmt.Println(slice)
    fmt.Printf("addres2: %p\n", &slice)

    slice = append(slice, 1, 2, 3)
    fmt.Printf("addres3: %p\n", &slice)

    slice = append(slice, 4, 5, 6)
    fmt.Printf("addres4: %p\n", &slice)
    fmt.Println(slice)
    }

    func main() {
    slice := make([]int, 1, 10)
    fmt.Printf("addres1: %p\n", &slice)
    someChage(slice)

    fmt.Printf("addres5: %p\n", &slice)
    fmt.Println(slice)
    }
    tiedan
        10
    tiedan  
       Jul 9, 2019
    只是共用了一个底层数组,但是是不同的切片
    tiedan
        11
    tiedan  
       Jul 9, 2019
    main 里面 slice 长度一直为 1 所以只能看到底层数组下标为 0 的那个元素
    flyzero
        12
    flyzero  
       Jul 9, 2019 via Android
    有说的不对的请指出,函数参数都值传递,但是切片传的是指针地址值,
    append 是在原有后面添加,所以没有改变指针值,你试试插入到第一位,就会变了
    hduwillsky
        13
    hduwillsky  
       Jul 9, 2019 via iPhone
    关键在 append 返回
    petelin
        14
    petelin  
       Jul 9, 2019 via iPhone
    @flyzero 也不会变 看看上面的回复 你的理解有问题
    mengzhuo
        15
    mengzhuo  
       Jul 9, 2019 via iPhone
    底层是数组,当你要 append 的数量大于数组大小 n 就会复制到 2n 的新数组中,这时 slice 里的指针发生变化。

    好好看看基础就不会有这样的问题了
    ruin2016
        16
    ruin2016  
       Jul 9, 2019
    ```
    func someChage(slice []int) {
    fmt.Printf("addres2: %p\n", &slice)

    slice = append(slice, 1, 2, 3)
    fmt.Printf("addres3: %p\n", &slice)

    slice = append(slice, 4, 5, 6)
    fmt.Printf("addres4: %p\n", &slice)
    fmt.Println(slice)
    }

    func main() {
    slice := make([]int, 0, 20)
    fmt.Printf("addres1: %p\n", &slice)
    someChage(slice)

    fmt.Printf("addres5: %p\n", &slice)
    fmt.Println(slice)
    }

    ```

    addres1: 0xc00007e040
    addres2: 0xc00007e060
    addres3: 0xc00007e060
    addres4: 0xc00007e060
    [1 2 3 4 5 6]
    addres5: 0xc00007e040
    []

    值引用!
    wewin
        17
    wewin  
    OP
       Jul 9, 2019
    @mengzhuo
    你说的,len 超过 cap 的时候,会创建一个新的底层数组,数组的 cap 会按照 len 的两倍增加。是下面这种情况:

    ```
    package main

    import "fmt"

    func someChage(slice []int) {
    fmt.Printf("addres2: %p\n", slice)

    slice = append(slice, 1, 2, 3)
    fmt.Printf("addres3: %p\n", slice)

    slice = append(slice, 4, 5, 6)
    fmt.Printf("addres4: %p\n", slice)
    fmt.Println(slice)
    }

    func main() {
    slice := make([]int, 0, 4)
    fmt.Printf("addres1: %p\n", slice)
    someChage(slice)

    fmt.Printf("addres5: %p\n", slice)
    fmt.Println(slice)
    }

    ```
    输出结果:
    ```
    addres1: 0xc00008a000
    addres2: 0xc00008a000
    addres3: 0xc00008a000
    addres4: 0xc000090000
    [1 2 3 4 5 6]
    addres5: 0xc00008a000
    []
    ```

    前前排大佬们的回复,那是些 get 到点的大佬们
    impl
        18
    impl  
       Jul 9, 2019 via Android
    因为 main 里面的 slice 的 len 是还是 0,把最后一句改成 fmt.Println(slice[:6])试试
    ScepterZ
        19
    ScepterZ  
       Jul 9, 2019
    slice 里存了地址 len cap 三个东西,你不穿指针进去的话,main 里的 slice 的 len 是没有改变的
    可以输出 slice 的地址就明白了
    shawn7
        20
    shawn7  
       Jul 9, 2019
    我还是不太明白,append 值不会改变,但是赋值可以

    package main

    import (
    "fmt"
    )

    func addSome(s []int) {
    s[0] = 1
    }

    func main() {
    s := make([]int, 5)
    fmt.Println(s)

    addSome(s)
    fmt.Println(s)
    }

    输出
    [0 0 0 0 0]
    [1 0 0 0 0]
    reage
        21
    reage  
       Jul 9, 2019
    slice := make([]int, 0, 20)
    你将 20 改为 2 看下,就看到变化。了解下 map 的 cap 参数
    rrfeng
        22
    rrfeng  
       Jul 9, 2019
    刚好前几天看到一篇文章,可以解惑。
    《 Go Slices are Fat Pointers 》
    https://nullprogram.com/blog/2019/06/30/
    shawn7
        23
    shawn7  
       Jul 9, 2019 via Android
    明白了,2 楼发的文章讲的很清楚。谢谢楼主
    jimmzhou
        25
    jimmzhou  
       Jul 9, 2019
    当把 slice 作为参数,本身传递的是值,但其内容就 byte* array,实际传递的是引用,所以可以在函数内部修改,但如果对 slice 本身做 append,而且导致 slice 进行了扩容,实际扩容的是函数内复制的一份切片,对于函数外面的切片没有变化。
    package main

    import(
    "fmt"
    )

    func someChage(slice *[]int) {
    fmt.Printf("addres2: %p\n", slice)

    *slice = append(*slice, 1, 2, 3)
    fmt.Printf("addres3: %p\n", slice)

    *slice = append(*slice, 4, 5, 6)
    fmt.Printf("addres4: %p\n", slice)
    fmt.Println(slice)
    }

    func main() {
    slice := make([]int, 0, 20)
    fmt.Printf("addres1: %p\n", slice)
    someChage(&slice)

    fmt.Printf("addres5: %p\n", slice)
    fmt.Println(*slice)
    }

    输出:
    addres1: 0xc00009c000
    addres2: 0xc00005e420
    addres3: 0xc00005e420
    addres4: 0xc00005e420
    [1 2 3 4 5 6]
    addres5: 0xc00009c000
    [1 2 3 4 5 6]
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   846 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 75ms · UTC 20:58 · PVG 04:58 · LAX 13:58 · JFK 16:58
    ♥ Do have faith in what you're doing.