Go 语言 在线

2160Go 语言切片(Slice)

切片内部结构:

struct Slice
{   
    byte*    array;       // actual data
    uintgo    len;        // number of elements
    uintgo    cap;        // allocated number of elements

};

第一个字段表示 array 的指针,是真实数据的指针第二个是表示 slice 的长度,第三个是表示 slice 的容量。

所以 unsafe.Sizeof(切片)永远都是 24。

当把 slice 作为参数,本身传递的是值,但其内容就 byte* array,实际传递的是引用,所以可以在函数内部修改,但如果对 slice 本身做 append,而且导致 slice 进行了扩容,实际扩容的是函数内复制的一份切片,对于函数外面的切片没有变化。

package main

import (
"fmt"
"unsafe"
)

func main() {
    slice_test := []int{1, 2, 3, 4, 5}
    fmt.Println(unsafe.Sizeof(slice_test))
    fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))
    slice_value(slice_test)
    fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))
    slice_ptr(&slice_test)
    fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))
    fmt.Println(unsafe.Sizeof(slice_test))
}

func slice_value(slice_test []int) {
    slice_test[1] = 100                // 函数外的slice确实有被修改
    slice_test = append(slice_test, 6) // 函数外的不变
    fmt.Printf("slice_value:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))
}

func slice_ptr(slice_test *[]int) { // 这样才能修改函数外的slice
    *slice_test = append(*slice_test, 7)
    fmt.Printf("slice_ptr:%#v,%#v,%#v\n", *slice_test, len(*slice_test), cap(*slice_test))
}

结果如下:

24
main:[]int{1, 2, 3, 4, 5},5,5
slice_value:[]int{1, 100, 3, 4, 5, 6},6,10
main:[]int{1, 100, 3, 4, 5},5,5
slice_ptr:[]int{1, 100, 3, 4, 5, 7},6,10
main:[]int{1, 100, 3, 4, 5, 7},6,10
24

2159Go 语言切片(Slice)

实例:

package main

import "fmt"

func main() {
   var array = []int{1, 2, 3, 4, 5}
   printSlice(array)
   slice := array[1:]
   printSlice(slice)
   array[1] = 100
   printSlice(slice)
}

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

以上代码执行输出结果为:

len=5 cap=5 slice=[1 2 3 4 5]
len=4 cap=4 slice=[2 3 4 5]
len=4 cap=4 slice=[100 3 4 5]

2158Go 语言切片(Slice)

使用 copy 函数要注意对于 copy(dst, src),要初始化 dst 的 size,否则无法复制。

错误示例:

dst := make([]int, 0)
src := []int{1, 2, 3}
copy(dst, src)
printSlice(src)
printSlice(dst)

func printSlice(x []int){
   fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}

输出结果:

len=3 cap=3 slice=[1 2 3]
len=0 cap=0 slice=[]

正确示例:

dst := make([]int, 3)  // 令size=3
src := []int{1, 2, 3}
copy(dst, src)
printSlice(src)
printSlice(dst)

输出结果:

len=3 cap=3 slice=[1 2 3]
len=3 cap=3 slice=[1 2 3]

2157Go 语言切片(Slice)

append() 和 copy() 部分,貌似有没说明白的地方。

numbers = [0, 1] 时,append(numbers, 2, 3, 4) 为什么 cap 从 2 变成 6 ?

经过实践得知,append(list, [params]),先判断 list 的 cap 长度是否大于等于 len(list) + len([params]),如果大于那么 cap 不变,否则 cap 等于 max{cap(list), cap[params]},所以当 append(numbers, 2, 3, 4) cap 从 2 变成 6。

2156Go 语言切片(Slice)

在做函数调用时,slice 按引用传递,array 按值传递:

package main

import "fmt"

func main(){
  changeSliceTest()
}

func changeSliceTest() {
    arr1 := []int{1, 2, 3}
    arr2 := [3]int{1, 2, 3}
    arr3 := [3]int{1, 2, 3}

    fmt.Println("before change arr1, ", arr1)
    changeSlice(arr1) // slice 按引用传递
    fmt.Println("after change arr1, ", arr1)

    fmt.Println("before change arr2, ", arr2)
    changeArray(arr2) // array 按值传递
    fmt.Println("after change arr2, ", arr2)

    fmt.Println("before change arr3, ", arr3)
    changeArrayByPointer(&arr3) // 可以显式取array的 指针
    fmt.Println("after change arr3, ", arr3)
}

func changeSlice(arr []int) {
    arr[0] = 9999
}

func changeArray(arr [3]int) {
    arr[0] = 6666
}

func changeArrayByPointer(arr *[3]int) {
    arr[0] = 6666
}

输出结果为:

before change arr1,  [1 2 3]
after change arr1,  [9999 2 3]
before change arr2,  [1 2 3]
after change arr2,  [1 2 3]
before change arr3,  [1 2 3]
after change arr3,  [6666 2 3]