array & slice

array & slice #

Slice append #

对 slice(切片)操作append时要小心。

  • 当 cap 容量还没有变化时,slice 是对数据本身的引用,append 会修改被引用的值
  • 当 slice 操作 append 使 cap 容量变化后,slice 会复制被引用的数组切断之前和数组的关系。
func main() {
	var array =[]int{1,2,3,4,5}// len:5,capacity:5
	var newArray=array[1:3]// len:2,capacity:4   (已经使用了两个位置,所以还空两位置可以append)
	fmt.Printf("%p\n",array)
	fmt.Printf("%p\n",newArray) // 可以看到newArray的地址指向的是array[1]的地址,即他们底层使用的还是一个数组
	fmt.Printf("%v\n",array) //[1 2 3 4 5]
	fmt.Printf("%v\n",newArray) //[2 3]

	newArray[1]=9 //更改后array、newArray都改变了
	fmt.Printf("%v\n",array) // [1 2 9 4 5]
	fmt.Printf("%v\n",newArray) // [2 9]

	newArray=append(newArray,11,12)//append 操作之后,array的len和capacity不变,newArray的len变为4,capacity:4。因为这是对newArray的操作
	fmt.Printf("%v\n",array) //[1 2 9 11 12] //注意对newArray做append操作之后,array[3],array[4]的值也发生了改变
	fmt.Printf("%v\n",newArray) //[2 9 11 12]

	newArray=append(newArray,13,14) // 因为newArray的len已经等于capacity,所以再次append就会超过capacity值,
	// 此时,append函数内部会创建一个新的底层数组(是一个扩容过的数组),并将array指向的底层数组拷贝过去,然后在追加新的值。
	fmt.Printf("%p\n",array)
	fmt.Printf("%p\n",newArray)
	array[1] = 10
	newArray[1] = 13
	fmt.Printf("%v\n",array) //[1 10 9 11 12]
	fmt.Printf("%v\n",newArray) //[2 13 11 12 13 14]  他两已经不再是指向同一个底层数组 array了
}

数组是 go 中的值类型,而 slice 是引用类型。

Array copy #

package main

import "fmt"

func main() {
	sample1 := [2]string{"a", "b"}
	fmt.Printf("Sample1 Before: %v\n", sample1)
	sample2 := sample1
	sample2[1] = "c"
	fmt.Printf("Sample1 After assignment: %v\n", sample1)
	fmt.Printf("Sample2: %v\n", sample2)
	test(sample1)
	fmt.Printf("Sample1 After Test Function Call: %v\n", sample1)
}
func test(sample [2]string) {
	sample[0] = "d"
	fmt.Printf("Sample in Test function: %v\n", sample)
}

output:

Sample1 Before: [a b]
Sample1 After assignment: [a b]
Sample2: [a c]
Sample in Test function: [d b]
Sample1 After Test Function Call: [a b]

Slice copy #

使用 copy 函数进行拷贝:

func copy(dst, src []Type) int
package main

import "fmt"

func main() {
    src := []int{1, 2, 3, 4, 5}
    dst := make([]int, 5)

    numberOfElementsCopied := copy(dst, src)
    fmt.Printf("Number Of Elements Copied: %d\n", numberOfElementsCopied)
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)

    //After changing numbers2
    dst[0] = 10
    fmt.Println("\nAfter changing dst")
    fmt.Printf("dst: %v\n", dst)
    fmt.Printf("src: %v\n", src)
}

output:

Number Of Elements Copied: 5
dst: [1 2 3 4 5]
src: [1 2 3 4 5]

After changing dst
dst: [10 2 3 4 5]
src: [1 2 3 4 5]

Slice internals #

Slice 结构:

A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).

image

References #