slice英文切片的意思,给黄瓜切片,给面包切片。这里的slice是给数组切片,截取数组的一部分。go语言的slice不但是一个动词,而且是一个名词,既一种数据结构类似于数组的数据结构,甚至底层就是用数组实现的。

        数组所有的优点slice都有,而且slice更加灵活,slice支持可以通过append向slice中追加元素,长度不够时会动态扩展,通过再次slice切片,可以得到得到更小的slice结构,可以迭代、遍历。

        数组是一组数据类型相同,连续且长度固定的数据项序列,slice底层也是一个数组,但是slice维护了一个指针属性,指向创建的数组。

slice底层是数组,它的数据结构如下:

// runtime/slice.go
type slice struct {
	array unsafe.Pointer // 数组指针
	len   int // 长度 
	cap   int // 最大容量
}

slice保存了底层数组的地址,数组的长度,以及最大容量。

第一种定义方式:

var var_name []var_type

未初始化之前指针默认为 nil,长度为 0,最大容量为0。我们可以通过append函数追加元素,而不是像数组那样长度固定,元素数量不能改变。

        

package main

import (
	"fmt"
)

func main() {
	var num []int
	if num == nil {
		fmt.Println(len(num), cap(num))
	} //输出 0 0

	num = append(num, 1)
	fmt.Println(len(num), cap(num))
	//输出 1 1
}

第二种定义方式:

var_name []type = make([]type, len) 或者 var_name := make([]type, len)

可以指定容量,其中 capacity 为可选参数,var_name []type = make([]type, len, capacity),如果不设定capacity,默认capacity等于len。

var num = make([]int, 3, 5)

结构如下:

slicem什么意思 slice是什么意思啊英语_go

我们可通过append添加元素,当元素数量超过最大容量时,会进行扩容,扩容到最大容量的1倍,如何扩容呢,就是找一块2倍的capacity的内存,将原来的数据拷贝过去,然后释放原来的内存。这个跟C++中的vector很像。

我们可以通过一个例子看出来:

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var num = make([]int, 3, 5)
	//打印底层数组首地址
	fmt.Println(unsafe.Pointer(&num[0]))
	num = append(num, 0)
    //地址没有变
	fmt.Println(unsafe.Pointer(&num[0]))
    //地址仍然没有变
	num = append(num, 3)
	fmt.Println(unsafe.Pointer(&num[0]))
    num = append(num, 4)  
    //地址发生变化
    fmt.Println(unsafe.Pointer(&num[0]))  
    fmt.Println(num)
    //输出 6 10
    fmt.Println(len(num),cap(num))
}

这个例子说明append的时候,只要没有超过最大容量,返回的slice的指针不变,capacity不变,底层数组的内存不变。

如果append超出最大容量,但是用一个新的slice去接返回值,原来的slice不会销毁,新的slice指向新分配的底层数组。

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	var num = make([]int, 3, 3)
	//打印底层数组首地址
	fmt.Println(unsafe.Pointer(&num[0]))
	num1 := append(num, 0)

	fmt.Println(unsafe.Pointer(&num[0]))

	fmt.Println(unsafe.Pointer(&num1[0]))

	fmt.Println(num)
	fmt.Println(num1)
}

slice可以缩小,就是切片,截取slice的一部分:

SLICE[index1:index2]

index是slice的下标,就是截取从index1到index2之间的元素,左闭右开

我们还可以指定slice的容量:

SLICE[index1:index2:index3]

如果不指定就是index1到底部的长度为新的slice的容量。

package main

import (
	"fmt"
	"unsafe"
)


func main() {
	var num = make([]int, 3, 5)
	nnum := num[1:3]
    //输出4 2
	fmt.Println("nnum:", cap(nnum), len(nnum))

	s := []int{1, 2, 3, 4}
    //s: 4 4
	fmt.Println("s:", cap(s), len(s))
	ns := s[1:3]
    //ns: 3 2
	fmt.Println("ns:", cap(ns), len(ns))
    //输出地址一样
	fmt.Println(unsafe.Pointer(&s[1]))
	fmt.Println(unsafe.Pointer(&ns[0]))
	s[1] = 20
    //输出 [1 20 3 4]
	fmt.Println(s)
    //输出[20 3]
	fmt.Println(ns)
}

结构如下

slicem什么意思 slice是什么意思啊英语_指针_02

Slice可以通过copy(dest, src)函数拷贝数据,返回值是成功拷贝的数据个数。

package main

import (
	"fmt"
)

func main() {
	s := []int{1, 2, 3, 4}
	var d = make([]int, 3, 3)
	n := copy(d, s)
	//3 [1 2 3]
	fmt.Println(n, d)
}

        go语言中的函数的参数传递是值传递,就是将数值拷贝一份赋值给实参。如果参数是slice,就是将slice的拷贝一份赋值给实参,slice本质是一个指针指向底层数组,所以可以通过实参改变slice的数值。

package main

import (
	"fmt"
)

func main() {
	s := []int{1, 2, 3, 4}
	//输出[1 2 3 4]
	fmt.Println(s)
	foo(s)
	//输出[2 3 4 5]
	fmt.Println(s)
}

func foo(s []int) {
	for i, _ := range s {
		s[i] += 1
	}
}