• Nuzar 置顶
    2019-02-22
    老师的行文用字非常好,不用改!
     1
     11
  • 清风徐来
    2018-08-29
    语言描述有点啰嗦太学术化,和我当时看go并发编程第二版开头几章同样的感觉,希望能更加精简一些,直接突出重点要好很多。
    
     157
  • melon
    2018-08-27
    初始时两个切片引用同一个底层数组,在后续操作中对某个切片的操作超出底层数组的容量时,这两个切片引用的就不是同一个数组了,比如下面这个例子:
    s1 := []int {1,2,3,4,5}
    s2 := s1[0:5]

    s2 = append(s2, 6)
    s1[3] = 30

    此时s1[3]的值为30, s2[3]的值仍然为4,因为s2的底层数组已是扩容后的新数组了。
    展开
    
     69
  • 小小笑儿
    2018-08-29
    切片缩容之后还是会引用底层的原数组,这有时候会造成大量缩容之后的多余内容没有被垃圾回收。可以使用新建一个数组然后copy的方式。

    作者回复: 没错

    
     20
  • sky
    2018-09-15
    老师您好!我对源码demo16中示例1、3实际运行结果与预期结果表示ok,但唯独示例2的运行结果觉得没有什么规则可供参考,为何不是下面我预期的结果呢,对于实际的运行结果表示不理解,还烦请老师有空帮忙解答下,感谢!

    代码如下:
    // 示例2
        s7 := make([]int, 1024)
        fmt.Printf("The capacity of s7: %d\n", cap(s7))
        s7e1 := append(s7, make([]int, 200)...)
        fmt.Printf("s7e1: len: %d, cap: %d\n", len(s7e1), cap(s7e1))
        s7e2 := append(s7, make([]int, 400)...)
        fmt.Printf("s7e2: len: %d, cap: %d\n", len(s7e2), cap(s7e2))
        s7e3 := append(s7, make([]int, 600)...)
        fmt.Printf("s7e3: len: %d, cap: %d\n", len(s7e3), cap(s7e3))
        fmt.Println()
    实际运行结果:
    The capacity of s7: 1024
    s7e1: len: 1224, cap: 1280
    s7e2: len: 1424, cap: 1696
    s7e3: len: 1624, cap: 2048

    预期运行结果:
    The capacity of s7: 1024
    s7e1: len: 1224, cap: 1280
    s7e2: len: 1424, cap: 1600
    s7e3: len: 1624, cap: 2000
    展开
     5
     16
  • Laughing
    2018-08-27
    1.当两个长度不一的切片使用同一个底层数组,并且两切片的长度均小于数组的容量时,对其中长度较小的一个切片进行append操作,但不超过底层数组容量,这时会影响长度较长切片中原来比较小切片多看到的值,因为底层数组被修改了。
    2. 可以截取切片的部分数据,然后创建新数组来缩容
    
     11
  • 有铭
    2018-08-27
    谢谢老师,今天这篇文才让我意识到以前对切片的认知是不全面的。但也带来一个新问题,大部分语言里,类似切片的数据结构的实质就是可变数组,他们都没有窗口这个设计,golang是为啥设计了窗口这个功能呢?这个功能在实际开发中能如何应用呢?我想golang这种极简设计思想的语言,绝不会搞多余设计,必然是有某种场景,不用切片的窗口就搞不定。但是我想不出是什么
    
     7
  • 涛哥爱学习
    2018-08-30
    老师可以多些图表在文章里,方便阅读
    
     4
  • 余泽锋
    2018-08-27
    1.底层数组的变动会影响多个切片
    2.每一次缩容都需要生成新的切片
    
     4
  • mrly
    2018-09-27
    老师,我对demo16的运行结果有疑惑,按1024*1.25*1.25*1.25来说,结果应该是这样:
    实际运行结果:
    The capacity of s7: 1024
    s7e1: len: 1224, cap: 1280
    s7e2: len: 1424, cap: 1696
    s7e3: len: 1624, cap: 2048

    预期运行结果:
    The capacity of s7: 1024
    s7e1: len: 1224, cap: 1280=1024*1.25
    s7e2: len: 1424, cap: 1600=1024*1.25*1.25
    s7e3: len: 1624, cap: 2000=1024*1.25*1.25*1.25

    为什么结果不一样呢?
    展开
    
     3
  • mateye
    2018-08-30

    老师您好,就像您说的,切片赋值的话会,如果完全赋值,会指向相同的底层数组,
        s1 :=[]int{1,2,3,4}
        s2 := s1[0:4]
        就像这样,这样的话改变s2会影响s1,如何消除这种影响呢

    作者回复: 可以用copy函数,或者自己深拷贝。

    
     3
  • wjq310
    2018-08-28
    老师,请问下demo16.go的示例三的几个cap值是怎么来的?看这后面的值,不像是2的指数倍。更奇怪的是,我在不同的地方运行(比如把代码贴到https://golang.org/go)得到的结果还不一样,不知道为什么,麻烦帮忙解答一下,感谢了

    作者回复: 每次发现容量不够都会翻一倍,你可以从头算一下。另外,一旦超过1024每次只会增大1.25倍。

    
     3
  • 宇智波悟天
    2019-10-14
    关于老 slice 容量大于等于 1024 时,没有严格按照1.25倍增长的问题,和大家一样有些困惑,上网查了下。给大家一个参考:
    向 slice 追加元素的时候,若容量不够,会调用 growslice 函数
    func growslice(et *_type, old slice, cap int) slice {
        // ……
        newcap := old.cap
        doublecap := newcap + newcap
        if cap > doublecap {
            newcap = cap
        } else {
            if old.len < 1024 {
                newcap = doublecap
            } else {
                for newcap < cap {
                    newcap += newcap / 4
                }
            }
        }
        // ……
        capmem = roundupsize(uintptr(newcap) * ptrSize)
        newcap = int(capmem / ptrSize)

    重点看最后两行代码。
    对 newcap 作了一个内存对齐,这个和内存分配策略相关。进行内存对齐之后,新 slice 的容量是要 大于等于 老 slice 容量的 2倍或者1.25倍的。
    展开
    
     2
  • 党
    2019-07-21
    总结一下:
    如果不扩容,新切片和所有基于该底层数组的切片,都对同一个数组进行操作,会相互影响。
    如果扩容,新切片的底层数组会新生成一个,因切片对该数组的操作不会影响原来的数组(原来数组没有引用可能已经被回收了)
    
     2
  • 陈悬高
    2018-10-14
    虽然 slice 间接引用了底层数组的元素,但是其指针、长度和容量却是它自己的属性。要更新一个 slice 的指针、长度或容量必须使用显式的赋值。从这个角度看,slice 并不是“纯粹”的引用类型,而是像下面这种聚合类型:

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

    所以,不仅是在调用 `append` 函数时需要更新 slice 变量。另外,对于任何函数,只要有可能改变 slice 的长度或者容量,或者使得 slice 指向不同的底层数组,都需要更新 slice 变量。
    展开
    
     2
  • Empty
    2018-09-26
    王老师,能解释一下demo16里面的第三个示例么
    
     2
  • 徐宁
    2018-09-06
    能不能少用点前者后者这类语言,很容易困惑又回头去看
    
     2
  • 许大
    2019-08-28
    老师 go中 make和new 有什么区别

    作者回复: make 是专门用来创建 slice、map、channel 的值的。它返回的是被创建的值,并且立即可用。

    new 是申请一小块内存并标记它是用来存放某个值的。它返回的是指向这块内存的指针,而且这块内存并不会被初始化。或者说,对于一个引用类型的值,那块内存虽然已经有了,但还没法用(因为里面没有针对那个值的数据结构)。

    所以,对于引用类型的值,不要用 new,能用 make 就用 make,不能用 make 就用复合字面量来创建。

    
     1
  • Michael
    2019-05-07
    第三次看了,老师加了好多图,感激不尽
    
     1
  • 轻轻的飞 ོ
    2018-12-24
    今天看了 SliceHeader 才终于理解老师说的知识。append操作每次其实都返回了一个新的 SliceHeader,只不过里面的Data数组可能是原来,也可能是新的(由是否扩容决定),这是新旧slice之间可能的惟一关联。所以append操作后的新slice一定要保存好,因为append操作对原来的slice没有任何影响。
    
     1
我们在线,来聊聊吧