复制切片的目的是什么?

huangapple go评论77阅读模式
英文:

What is the point of copying a slice?

问题

这段代码的目的是将切片 s 扩容并复制到新的切片 t 中。通过循环遍历 s 的每个元素,将其逐个复制到 t 中。然后将 s 指向新的切片 t

你提到的第二段代码,通过 t = s[:] 将切片 s 赋值给 t,实际上是将 ts 指向了同一个底层数组。这样做的话,如果修改了 t 中的元素,s 中的对应元素也会被修改,因为它们共享同一个底层数组。所以在这种情况下,需要先创建一个新的切片 t,并将 s 中的元素逐个复制到 t 中,以确保 ts 是独立的。

至于你提到的 copy 函数,它确实会复制切片的底层数组。在第二段代码中,copy(c, b) 将切片 b 的内容复制到切片 c 中,包括底层数组的内容。这样做可以确保在返回 c 后,不再引用 b 的底层数组,避免潜在的内存泄漏问题。

英文:

What is the point of this snippet of code:

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

It's from this page: http://blog.golang.org/go-slices-usage-and-internals, and is supposed to grow a slice. However, above that code snippet is a diagram which depicts a slice as a struct with a pointer, a length, and a capacity. Why does each individual entry have to be copied over instead of something like:

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

And if the problem is that the capacity of t is changed to be the same as s, why isn't there another way of setting the pointers to be the same. Or does a slice have a pointer to every single element in the array within its bounds?

Edit: I read a little further and got to this snippet of code:

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

Its purpose is to stop referencing the file after c is returned by using copy. Does this imply that copy copies the underlying array as well as the slice?

答案1

得分: 8

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

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]

> Go编程语言规范
>
> 追加和复制切片
>
> 函数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]
英文:

To construct a new, higher capacity underlying array with the same length and values as the old underlying array. The old underlying array will be reclaimed by the garbage collector. For example,

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

Output:

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
>
> The function copy copies slice elements from a source src to a
> destination dst and returns the number of elements copied. Both
> arguments must have identical element type T and must be assignable to
> a slice of type []T. The number of elements copied is the minimum of
> len(src) and len(dst).
>
> Examples:
>
> 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")

If we return a reference to b, we pin the whole underlying array for b. Since b refers to a file, that could easily be megabytes or gigabytes. By returning a new underlying array c, which is the exact size of the number, a few bytes, there will no longer be a reference to the large underlying array for b and it will be reclaimed by the garbage collector. The copy built-in function copies values from b to c. For example,

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

Output:

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]

huangapple
  • 本文由 发表于 2014年5月29日 08:51:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/23924125.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定