【问题标题】:What is the point of copying a slice?复制切片有什么意义?
【发布时间】:2014-05-29 00:51:07
【问题描述】:

这段sn-p代码有什么意义:

t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0
for i := range s {
    t[i] = s[i]
}
s = t

来自这个页面:http://blog.golang.org/go-slices-usage-and-internals,应该长出一片。然而,上面的代码 sn-p 是一个图表,它将切片描述为具有指针、长度和容量的结构。为什么每个单独的条目都必须被复制而不是像这样的:

t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0
t = s[:]
s = t

如果问题是t 的容量更改为与s 相同,为什么没有另一种方法将指针设置为相同。或者切片是否有指向其边界内数组中每个元素的指针?

编辑:我进一步阅读并得到了这个sn-p代码:

func CopyDigits(filename string) []byte {
    b, _ := ioutil.ReadFile(filename)
    b = digitRegexp.Find(b)
    c := make([]byte, len(b))
    copy(c, b)
    return c
}

其目的是在使用copy 返回c 后停止引用文件。这是否意味着copy 复制了底层数组以及切片?

【问题讨论】:

    标签: go


    【解决方案1】:

    构造一个新的、容量更大的底层数组,其长度和值与旧底层数组相同。旧的底层数组将被垃圾收集器回收。例如,

    package main
    
    import "fmt"
    
    func main() {
        s := []byte{0, 1, 2, 3, 4}[:3]
        fmt.Printf("s: %p %d %v %d %v\n", &s[0], len(s), s, cap(s), s[:cap(s)])
        t := make([]byte, len(s), (cap(s)+1)*2) // +1 in case cap(s) == 0
        fmt.Printf("t: %p %d %v %d %v\n", &t[0], len(t), t, cap(t), t[:cap(t)])
        for i := range s {
            t[i] = s[i]
        }
        s = t
        fmt.Printf("s: %p %d %v %d %v\n", &s[0], len(s), s, cap(s), s[:cap(s)])
        fmt.Printf("t: %p %d %v %d %v\n", &t[0], len(t), t, cap(t), t[:cap(t)])
    }
    

    输出:

    s: 0x10500168 3 [0 1 2] 5 [0 1 2 3 4]
    t: 0x1052e130 3 [0 0 0] 12 [0 0 0 0 0 0 0 0 0 0 0 0]
    s: 0x1052e130 3 [0 1 2] 12 [0 1 2 0 0 0 0 0 0 0 0 0]
    t: 0x1052e130 3 [0 1 2] 12 [0 1 2 0 0 0 0 0 0 0 0 0]
    

    The Go Programming Language Specification

    Appending to and copying slices

    函数 copy 将切片元素从源 src 复制到 目标 dst 并返回复制的元素数。两个都 参数必须具有相同的元素类型 T 并且必须可分配给 []T 类型的切片。复制的元素数量是最小的 len(src) 和 len(dst)。

    例子:

    var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
    var s = make([]int, 6)
    var b = make([]byte, 5)
    n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
    n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
    n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")
    

    如果我们返回对b 的引用,我们会将整个底层数组固定为b。因为b 指的是一个文件,所以很容易达到兆字节或千兆字节。通过返回一个新的底层数组c,它是数字的确切大小,几个字节,将不再引用b 的大型底层数组,它将被垃圾收集器回收。 copy 内置函数将值从 b 复制到 c。例如,

    package main
    
    import "fmt"
    
    func Copy() []byte {
        b := []byte{0, 1, 2, 3, 4, 5, 6, 7}
        fmt.Printf("b: %p %d %v %d %v\n", &b[0], len(b), b, cap(b), b[:cap(b)])
        b = b[:2]
        fmt.Printf("b: %p %d %v %d %v\n", &b[0], len(b), b, cap(b), b[:cap(b)])
        c := make([]byte, len(b))
        copy(c, b)
        fmt.Printf("c: %p %d %v %d %v\n", &c[0], len(c), c, cap(c), c[:cap(c)])
        return c
    }
    
    func main() {
        d := Copy()
        fmt.Printf("d: %p %d %v %d %v\n", &d[0], len(d), d, cap(d), d[:cap(d)])
    }
    

    输出:

    b: 0x10500168 8 [0 1 2 3 4 5 6 7] 8 [0 1 2 3 4 5 6 7]
    b: 0x10500168 2 [0 1] 8 [0 1 2 3 4 5 6 7]
    c: 0x10500178 2 [0 1] 2 [0 1]
    d: 0x10500178 2 [0 1] 2 [0 1]
    

    【讨论】:

      猜你喜欢
      • 2018-01-07
      • 2019-08-13
      • 1970-01-01
      • 2012-08-07
      • 2011-11-10
      • 2010-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多