一、说明
切片(slice)是golang里面的可变长元素类型,不是数组或数组指针,本质是一个结构体。
slice的声明如下(位于安装目录下的src/runtime/slice.go
):
type slice struct {
array unsafe.Pointer //array是一个指针,指向实际的数据地址。
len int //slice的长度
cap int //slice的容量
}
值得注意的是,切片除了有一个len
属性表明当前切片长度以外,还有一个容量标识cap
,表示切片的最大容量,len
小于等于cap
。查看长度和容量的方法很简单,直接使用len()
和cap()
即可。
二、创建方式
1.直接创建
s := []int{1, 2, 3, 4, 5} //len(s)=5,cap(s)=5
也可以使用索引号指定
s := []int{1, 2, 3, 8:999} //len(s))=9, cap(s)=9, s:[1 2 3 0 0 0 0 0 999]
2.基于数组创建
通过给定数组的下标范围来创建切片,创建的时候,切片长度等于我们实际赋值的长度,容量默认等于数组第一个被赋值的元素
到最后一个元素
的距离:
data := [...]int{0, 1, 8, 3, 4, 5, 6, 7}
s_data := data[1:5] //len(s_data)=4, cap(s_data)=7, s_data:[1, 8, 3, 4]
这个距离也可以显式指定:
data := [...]int{0, 1, 8, 3, 4, 5, 6, 7}
s_data := data[1:5:5] //len(s_data)=4, cap(s_data)=4, s_data:[1, 8, 3, 4]
s_data := data[x:y:z]
的含义如下:
- x表示赋值的起始索引,从x开始赋值。
- y表示赋值的结束索引,长度就是x-y的距离。
- z表示容量最大可以到的索引,x-z的距离是容量。
通过数组创建的切片和数组共享同一块内存区域,修改切片的时候数组也会随之改变:
arr := [...]int{1, 2, 3, 4, 5}
s := arr[:4]
fmt.Println(s) // [1 2 3 4]
s[1] = 99
fmt.Println(s[1], arr[1]) //99 99
3.使用make创建
语法形式是make([]Type, Len, Cap),
Type是想要创建的切片类型,Len
是切片长度,Cap
是切片容量。
s := make([]int, 6, 8)
fmt.Println(len(s), cap(s)) //6, 8
cap不是必需的,可以省略不写,默认等于len:
s := make([]int, 6)
fmt.Println(len(s), cap(s)) //6, 6
三、使用示例
package main
import "fmt"
func main(){
s := []int{1, 2, 3, 4, 5, 6} //len(s))=9, cap(s)=9
fmt.Println(s[0], s[1]) //直接使用索引
fmt.Println(s[2:3]) //索引范围[2, 3)
fmt.Println(s[4:]) //[4, last]
fmt.Println(s[:6]) //[start, 6)
s2 := s[1:5]
fmt.Println(s2)
}
输出:
1 2
[3]
[5 6]
[1 2 3 4 5 6]
[2 3 4 5]
四、和数组定义的区别
1.数组的定义:
s := [3]int{1, 2, 3}
s := [...]int{1, 2, 3}
2.切片的定义:
s := []int{1, 2, 3}
s := make([]s, 3)
五、注意事项
切片本质是一个结构体,使用时拷贝传递。在使用切片做函数形参时,会创建一个临时变量,但内部的数据还是不变。
package main
import "fmt"
func f(a []int){
fmt.Printf("%p %p %p", &a, &a[0], &a[1])
}
func main(){
s := []int{1, 2, 3, 4, 5, 6}
fmt.Printf("%p %p %p", &s, &s[0], &s[1])
f(s)
}
输出:
0xc04203e400 0xc04203bf50 0xc04203bf58
0xc04203e460 0xc04203bf50 0xc04203bf58
可以看到s的地址变了,但是内部s[0]
, s[1]
的地址还是一样的,说明在创建副本的时候只是简单拷贝了切片的值,并没有把内部的内存空间重新分配。所以当我们使用range
或者把切片作为函数形参时在代码块内部直接就会修改切片的值。
但是在要注意的是在把切片作为函数形参时,在函数内部使用append
就不会对切片产生影响:
package main
import (
"fmt"
)
func f(a []string){
a = append(a, "hello")
}
func main(){
a := make([]string, 0, 4)
f(a)
fmt.Println(a) //输出[],而不是[hello]
}
原理很简单,因为append
会返回一个新的slice给a
,而a
是s
的一个副本,所以这里对s
就不会有任何影响,此时要想修改s
可以把切片改成指针类型:
package main
import (
"fmt"
)
func f(a *[]string){
*a = append(*a, "hello")
}
func main(){
a := make([]string, 0, 4)
f(&a)
fmt.Println(a) //[hello]
}
此处评论已关闭