这是我参与11月更文挑战的第26天,活动详情查看:2021最后一次更文挑战
切片
切片是数组的封装,为实现数据序列类型,提供更通用、更强大、更方便的接口。 除了具有显式维度的项(例如转换矩阵),Go 中的大多数数组编程都是使用切片而不是简单数组完成的。
切片保存对底层数组的引用,如果将一个切片分配给另一个切片,则两者都引用同一个数组。 如果一个函数接受一个切片参数,它对切片元素所做的更改将对调用者可见,类似于传递一个指向底层数组的指针。 因此,Read 函数可以接受切片参数而不是指针和计数; 切片内的长度设置了读取数据量的上限。 下面是os包中File类型的Read方法的签名:
func (file *File) Read(buf []byte) (n int, err error)
该方法返回读取的字节数和错误值(如果有)。 要读入较大缓冲区 buf 的前 32 个字节,请对缓冲区进行切片(此处用作动词)。
n, err := f.Read(buf[0:32])
这种切片的方法常用且高效。若不谈效率,以下片段同样能读取该缓冲区的前 32 个字节。
var n int
var err error
for i := 0; i < 32; i++ {
nbytes, e := f.Read(buf[i:i+1]) // Read one byte.
if nbytes == 0 || e != nil {
err = e
break
}
n += nbytes
}
只要不超过底层数组的限制;切片的长度可以改变。 只需将其分配给自身的一部分即可。 切片的容量,可通过内置函数 cap 访问,会返回切片可能采用的最大长度。 这是一个将数据附加到切片的函数。 如果数据超过容量,则重新分配切片。 返回结果切片。 当切片是nil的时候使用 len 和 cap 也是合法的,并返回 0。
func Append(slice, data[]byte) []byte {
l := len(slice)
if l + len(data) > cap(slice) { // 重新分配
// 为了后面的增长,需分配两份。
newSlice := make([]byte, (l+len(data))*2)
// copy 函数是预声明的,且可用于任何切片类型。
copy(newSlice, slice)
slice = newSlice
}
slice = slice[0:l+len(data)]
for i, c := range data {
slice[l+i] = c
}
return slice
}
必须最后返回切片,因为尽管 Append 可以修改 slice 中元素,但切片自己(其运行时数据结构包含指针、长度和容量)是通过值传递的。
向切片追加东西的想法非常有用,因此有专门的内建函数 append。 理解该函数的设计,我们还需要一些额外的信息,我们稍后再介绍它。