1、Go语言切片Slice
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切
片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
1.1 定义切片
var identifier []type
var slice1 []type = make([]type, len)
// 也可以简写为
slice1 := make([]type, len)
make([]T, length, capacity)
1.2 切片初始化
s :=[] int {1,2,3 }
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。
s := arr[:]
s := arr[startIndex:endIndex]
将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。
s := arr[startIndex:]
默认 endIndex 时将表示一直到arr的最后一个元素。
s := arr[:endIndex]
默认 startIndex 时将表示从 arr 的第一个元素开始。
s1 := s[startIndex:endIndex]
s :=make([]int,len,cap)
通过内置函数 make() 初始化切片 s,[]int 标识为其元素类型为 int 的切片。
1.3 len() 和 cap() 函数
切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。
package main
import "fmt"
func main() {
var numbers = make([]int, 3, 5)
printSlice(numbers)
}
func printSlice(x []int) {
// len=3 cap=5 slice=[0 0 0]
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.4 空(nil)切片
package main
import "fmt"
func main() {
var numbers []int
// len=0 cap=0 slice=[]
printSlice(numbers)
if numbers == nil {
// 切片是空的
fmt.Printf("切片是空的")
}
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.5 切片截取
可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]。
package main
import "fmt"
func main() {
/* 创建切片 */
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8}
// len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]
printSlice(numbers)
/* 打印原始切片 */
// numbers == [0 1 2 3 4 5 6 7 8]
fmt.Println("numbers ==", numbers)
/* 打印子切片从索引1(包含) 到索引4(不包含)*/
// numbers[1:4] == [1 2 3]
fmt.Println("numbers[1:4] ==", numbers[1:4])
/* 默认下限为 0*/
// numbers[:3] == [0 1 2]
fmt.Println("numbers[:3] ==", numbers[:3])
/* 默认上限为 len(s)*/
// numbers[4:] == [4 5 6 7 8]
fmt.Println("numbers[4:] ==", numbers[4:])
numbers1 := make([]int, 0, 5)
// len=0 cap=5 slice=[]
printSlice(numbers1)
/* 打印子切片从索引 0(包含) 到索引 2(不包含) */
number2 := numbers[:2]
// len=2 cap=9 slice=[0 1]
// cap=9-0=9(0为start的下标)
printSlice(number2)
/* 打印子切片从索引 2(包含) 到索引 5(不包含) */
number3 := numbers[2:5]
// len=3 cap=7 slice=[2 3 4]
// cap=9-2=7(2为start的下标)
printSlice(number3)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
我们可以看出切片,实际的是获取数组的某一部分,len切片<=cap切片<=len数组,切片由三部分组成:指向底
1.6 append() 和 copy() 函数
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。
package main
import "fmt"
func main() {
var numbers []int
// len=0 cap=0 slice=[]
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
// len=1 cap=1 slice=[0]
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
// len=2 cap=2 slice=[0 1]
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2, 3, 4)
// len=5 cap=6 slice=[0 1 2 3 4]
// len(list)+len([params])为奇数
// cap=len(list)+len([params])+1=2+3+1=6
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1, numbers)
// len=5 cap=12 slice=[0 1 2 3 4]
printSlice(numbers1)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
package main
import "fmt"
func main() {
var arr1 = []int{1, 2, 3}
var arr2 = []int{4, 5, 6}
var arr3 = []int{7, 8, 9}
var s1 = append(append(arr1, arr2...), arr3...)
// s1: [1 2 3 4 5 6 7 8 9]
fmt.Printf("s1: %vn", s1)
}
使用 copy 函数要注意对于 copy(dst, src),要初始化 dst 的 size,否则无法复制。
// 错误示例
package main
import "fmt"
func main() {
dst := make([]int, 0)
src := []int{1, 2, 3}
copy(dst, src)
// len=3 cap=3 slice=[1 2 3]
printSlice(src)
// len=0 cap=0 slice=[]
printSlice(dst)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
// 正确示例
package main
import "fmt"
func main() {
// 令size=3
dst := make([]int, 3)
src := []int{1, 2, 3}
copy(dst, src)
// len=3 cap=3 slice=[1 2 3]
printSlice(src)
// len=3 cap=3 slice=[1 2 3]
printSlice(dst)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.7 切片截取的长度问题
我们基于原数组或者切片创建一个新的切片后,那么新的切片的大小和容量是多少呢?
这里有个公式,对于底层数组容量是 k 的切片 slice[i:j] 来说:
长度: j-i
容量: k-i
实例:
package main
import "fmt"
func main() {
numbers := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// len=11 cap=11 slice=[0 1 2 3 4 5 6 7 8 9 10]
printSlice(numbers)
// [1 2]
fmt.Printf("%dn", numbers[1:3])
// [2 3 4 5 6]
fmt.Printf("%dn", numbers[2:7])
// [0 1 2]
fmt.Printf("%dn", numbers[:3])
// [4 5 6 7 8 9 10]
fmt.Printf("%dn", numbers[4:])
number1 := make([]int, 0, 5)
number2 := numbers[:3]
// len=0 cap=5 slice=[]
printSlice(number1)
// len=3 cap=11 slice=[0 1 2]
// cap=11-0=11
printSlice(number2)
number3 := numbers[2:5]
// len=3 cap=9 slice=[2 3 4]
// capacity为9是因为number3的ptr指向第2个元素,后面还剩2,3,4,5,6,7,8,9,10, 所以 cap=9
// cap=11-2=9
printSlice(number3)
number4 := numbers[3:8]
// len=5 cap=8 slice=[3 4 5 6 7]
// cap=11-3=8
printSlice(number4)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.8 append函数长度问题
package main
import "fmt"
func main() {
numbers := []int{0, 1}
// numbers的容量: 2
fmt.Println("numbers的容量: ", cap(numbers))
// numbers的长度: 2
fmt.Println("numbers的长度: ", len(numbers))
numbers = append(numbers, 2, 3, 4)
// numbers的容量: 6
fmt.Println("numbers的容量: ", cap(numbers))
// numbers的长度: 5
fmt.Println("numbers的长度: ", len(numbers))
}
当 numbers = [0, 1] 时,append(numbers, 2, 3, 4) 为什么 cap 从 2 变成 6?
append(list, [params]) 最终cap的计算:
len(list)+len([params])为偶数: cap=len(list)+len([params])
len(list)+len([params])为奇数: cap=len(list)+len([params])+1
即 cap 始终为偶数。
len(list)+1<=cap: cap=cap
len(list)+1>cap: cap=2*cap
即 cap 总是呈 2 倍的增加(也是偶数)。
通过 append() 函数向数组中添加元素,首先 cap 会以二倍的速度增长,如果发现增长 2 倍以上的容量可以满足扩
容后的需求,那么 cap*2,否则就会看扩容后数组的 length 是多少 cap=length+1。
每次cap改变的时候指向array内存的指针都在变化,当在使用 append 的时候,如果 cap==len 了这个时候就会新
开辟一块更大内存,然后把之前的数据复制过去(实际go在append的时候放大cap是有规律的,在 cap 小于1024
的情况下是每次扩大到 2 * cap ,当大于1024之后就每次扩大到 1.25 * cap)。
通过查看$GOROOT/src/runtime/slice.go
源码:
// cap为需要的容量,即新申请的容量
newcap := old.cap
doublecap := newcap + newcap
if cap > doublecap {
newcap = cap
} else {
const threshold = 256
if old.cap < threshold {
newcap = doublecap
} else {
// Check 0 < newcap to detect overflow
// and prevent an infinite loop.
for 0 < newcap && newcap < cap {
// Transition from growing 2x for small slices
// to growing 1.25x for large slices. This formula
// gives a smooth-ish transition between the two.
newcap += (newcap + 3*threshold) / 4
}
// Set newcap to the requested cap when
// the newcap calculation overflowed.
if newcap <= 0 {
newcap = cap
}
}
}
1、首先判断,如果新申请容量(cap)大于2倍的旧容量(old.cap),最终容量(newcap)就是新申请的容量(cap)。
2、否则,如果旧切片的长度小于阈值,则最终容量(newcap)就是旧容量(old.cap)的两倍,即
3、否则,如果旧切片长度大于等于阈值,则最终容量(newcap)从旧容量(old.cap)开始循环增加为原来的1.25
倍,即
newcap += (newcap + 3*threshold) / 4
,直到最终容量(newcap)大于等于新申请的容量(cap),即(
newcap >= cap
)。
package main
import "fmt"
func main() {
var numbers []int
// len=0 cap=0 slice=[]
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
// len=1 cap=1 slice=[0]
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
// len=2 cap=2 slice=[0 1]
printSlice(numbers)
/* 注意cap容量的变化 */
numbers = append(numbers, 2)
// len=3 cap=4 slice=[0 1 2]
printSlice(numbers)
numbers = append(numbers, 3)
// len=4 cap=4 slice=[0 1 2 3]
printSlice(numbers)
// 可以看出,容量不够时,cap会自动扩容到2倍
numbers = append(numbers, 4)
// len=5 cap=8 slice=[0 1 2 3 4]
printSlice(numbers)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.9 切片是引用传值
在做函数调用时,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}
// before change arr1, [1 2 3]
fmt.Println("before change arr1, ", arr1)
// slice 按引用传递
changeSlice(arr1)
// after change arr1, [9999 2 3]
fmt.Println("after change arr1, ", arr1)
// before change arr2, [1 2 3]
fmt.Println("before change arr2, ", arr2)
// array 按值传递
changeArray(arr2)
// after change arr2, [1 2 3]
fmt.Println("after change arr2, ", arr2)
// before change arr3, [1 2 3]
fmt.Println("before change arr3, ", arr3)
// 可以显式取array的指针
changeArrayByPointer(&arr3)
// after change arr3, [6666 2 3]
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
}
1.10 切片内部结构
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}
// 24
fmt.Println(unsafe.Sizeof(slice_test))
// main:[]int{1, 2, 3, 4, 5},5,5
fmt.Printf("main:%#v,%#v,%#vn", slice_test, len(slice_test), cap(slice_test))
// slice_value:[]int{1, 100, 3, 4, 5, 6},6,10
slice_value(slice_test)
// main:[]int{1, 100, 3, 4, 5},5,5
fmt.Printf("main:%#v,%#v,%#vn", slice_test, len(slice_test), cap(slice_test))
// slice_ptr:[]int{1, 100, 3, 4, 5, 7},6,10
slice_ptr(&slice_test)
// main:[]int{1, 100, 3, 4, 5, 7},6,10
fmt.Printf("main:%#v,%#v,%#vn", slice_test, len(slice_test), cap(slice_test))
// 24
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,%#vn", 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,%#vn", *slice_test, len(*slice_test), cap(*slice_test))
}
slice 的底层是数组指针,所以 slice a 和 s 指向的是同一个底层数组,所以当修改 s时,a 也会被修改;修改a时,
s也会被修改。
实例1:
package main
import "fmt"
func main() {
// len=3, cap=3
s := []int{1, 2, 3}
a := s
s[0] = 888
// [888 2 3] 3 3
fmt.Println(a, len(a), cap(a))
// [888 2 3] 3 3
fmt.Println(s, len(s), cap(s))
// append添加的元素对a不生效
s = append(s, 4)
// [888 2 3 4] 4 6
fmt.Println(s, len(s), cap(s))
// [888 2 3] 3 3
fmt.Println(a, len(a), cap(a))
}
实例2:
package main
import "fmt"
func main() {
var array = []int{1, 2, 3, 4, 5}
// len=5 cap=5 slice=[1 2 3 4 5]
printSlice(array)
slice := array[1:]
// len=4 cap=4 slice=[2 3 4 5]
printSlice(slice)
array[1] = 100
// len=4 cap=4 slice=[100 3 4 5]
printSlice(slice)
// len=5 cap=5 slice=[1 100 3 4 5]
printSlice(array)
slice[2] = 1000
// len=5 cap=5 slice=[1 100 3 1000 5]
printSlice(array)
// len=4 cap=4 slice=[100 3 1000 5]
printSlice(slice)
}
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%vn", len(x), cap(x), x)
}
1.11 三个参数的切片
做 slice 截取时建议用两个参数,尤其是从底层数组进行切片操作时,因为这样在进行第一次 append 操作时,会
s = s[low : high : max]
切片的三个参数的切片截取的意义为 low 为截取的起始下标(含), high 为窃取
的结束下标(不含 high),max 为切片保留的原切片的最大下标(不含 max);即新切片从老切片的 low 下标元
素开始,len = high - low
, cap = max - low
;high 和 max 一旦超出在老切片中越界,就会发生 runtime
err,slice out of range。另外如果省略第三个参数的时候,第三个参数默认和第二个参数相同,即 len = cap。
实例:
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
s = s[1:9:10]
// [1 2 3 4 5 6 7 8]
fmt.Println(s)
// 8
fmt.Println(len(s))
// 9
fmt.Println(cap(s))
}
package main
import "fmt"
func main() {
s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
// 修改 max 值为 13
s = s[1:9:13]
fmt.Println(s)
fmt.Println(len(s))
fmt.Println(cap(s))
}
// 报错
panic: runtime error: slice bounds out of range [::13] with capacity 10
goroutine 1 [running]:
1.12 append会让切片和与它相关的切片脱钩,但地址不变
&会让原切片发生改变,切片也会让原切片发生改变,append不会发生改变:
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4}
b := a
// part1 a len=4 cap=4 slice=[1 2 3 4]
printSlice(a, "part1 a")
// part1 b len=4 cap=4 slice=[1 2 3 4]
printSlice(b, "part1 b")
fmt.Printf("n")
a[0] = 9
// part2 a len=4 cap=4 slice=[9 2 3 4]
printSlice(a, "part2 a")
// part2 b len=4 cap=4 slice=[9 2 3 4]
printSlice(b, "part2 b")
fmt.Printf("n")
// [9 2 3 4 5]
a = append(a, 5)
a[0] = 1
// part3 a len=5 cap=8 slice=[1 2 3 4 5]
printSlice(a, "part3 a")
// part3 b len=4 cap=4 slice=[9 2 3 4]
printSlice(b, "part3 b")
fmt.Printf("n")
c := a
d := &a
// part4 a len=5 cap=8 slice=[1 2 3 4 5]
printSlice(a, "part4 a")
// part4 c len=5 cap=8 slice=[1 2 3 4 5]
printSlice(c, "part4 c")
// part4 *d len=5 cap=8 slice=[1 2 3 4 5]
printSlice(*d, "part4 *d")
fmt.Printf("n")
// [1 2 3 4 5 6]
a = append(a, 6)
// part5 a len=6 cap=8 slice=[1 2 3 4 5 6]
printSlice(a, "part5 a")
// part5 c len=5 cap=8 slice=[1 2 3 4 5]
printSlice(c, "part5 c")
// part5 *d len=6 cap=8 slice=[1 2 3 4 5 6]
printSlice(*d, "part5 *d")
}
func printSlice(x []int, y string) {
fmt.Printf("%v len=%d cap=%d slice=%vn", y, len(x), cap(x), x)
}
猜测脱钩的情况是由于切片底层数组扩张(创建了新数组替换旧数组)导致:
package main
import "fmt"
func main() {
x := make([]int, 4)
a := x[:2]
a[0] = 1
// [1 0]
fmt.Println(a)
// [1 0 0 0]
fmt.Println(x)
// [1 0 2]
a = append(a, 2)
a[0] = 0
// [0 0 2]
fmt.Println(a)
// [0 0 2 0]
fmt.Println(x)
// 切片a的地址:0xc00000e1a0
fmt.Printf("切片a的地址:%pn", a)
// 切片x的地址:0xc00000e1a0
fmt.Printf("切片x的地址:%pn", x)
fmt.Println()
y := make([]int, 4)
b := y
b[0] = 1
// [1 0 0 0]
fmt.Println(b)
// [1 0 0 0]
fmt.Println(y)
// [1 0 0 0 2]
b = append(b, 2)
b[0] = 0
// [0 0 0 0 2]
fmt.Println(b)
// [1 0 0 0]
fmt.Println(y)
// 切片b的地址:0xc000014240
fmt.Printf("切片b的地址:%pn", b)
// 切片y的地址:0xc00000e1e0
fmt.Printf("切片y的地址:%pn", y)
fmt.Println()
}
脱钩:b:=a,修改a的值b的值不会改变,正常情况下是要改变的。
在 cap(b)<len(a)
的情况下会发生脱钩,但是在 cap(b)≥len(a)
时,append并不能使切片脱钩。
通过 b:=a
引用的方式,当 cap(b) < len(a)
,修改 a[i]
的值并不会改变 b[i]
的值,发生了脱钩;但是如果
cap(b) ≥ len(a)
时,修改 a[i]
的值就会改变 b[i]
的值,没有发生脱钩;要想使两个切片同步改变,最好的
package main
import (
"fmt"
)
func main() {
a := []int{1, 2, 3, 4}
b := a
// part1 a len=4 cap=4 slice=[1 2 3 4]
printSlice(a, "part1 a")
// part1 b len=4 cap=4 slice=[1 2 3 4]
printSlice(b, "part1 b")
fmt.Printf("n")
a[0] = 9
// part2 a len=4 cap=4 slice=[9 2 3 4]
printSlice(a, "part2 a")
// part2 b len=4 cap=4 slice=[9 2 3 4]
printSlice(b, "part2 b")
fmt.Printf("n")
a = append(a, 5)
a[0] = 1
// part3 a len=5 cap=8 slice=[1 2 3 4 5]
printSlice(a, "part3 a")
// part3 b len=4 cap=4 slice=[9 2 3 4]
printSlice(b, "part3 b")
fmt.Printf("n")
}
func printSlice(x []int, y string) {
fmt.Printf("%v len=%d cap=%d slice=%vn", y, len(x), cap(x), x)
}
在 part3 中,通过 a[0]=1 修改了 a[0] 的值,但是 b[0] 的值并没有改变;通过 a=append(a,5) 增加了一个数
据,b 切片没有增加数据;这只是在 b 的 cap 比较小的情况下才会出现的情况;如果 b 的 cap 足够大呢?
将代码修改成:
package main
import (
"fmt"
)
func main() {
a := make([]int, 4, 10)
// part1 a len=4 cap=10 slice=[0 0 0 0]
printSlice(a, "part1 a")
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
b := a
// part1 a len=4 cap=10 slice=[1 2 3 4]
printSlice(a, "part1 a")
// part1 b len=4 cap=10 slice=[1 2 3 4]
printSlice(b, "part1 b")
fmt.Printf("n")
a[0] = 99
// part2 a len=4 cap=10 slice=[99 2 3 4]
printSlice(a, "part2 a")
// part2 b len=4 cap=10 slice=[99 2 3 4]
printSlice(b, "part2 b")
fmt.Printf("n")
a = append(a, 5)
a[0] = 100
// part3 a len=5 cap=10 slice=[100 2 3 4 5]
printSlice(a, "part3 a")
// part3 b len=4 cap=10 slice=[100 2 3 4]
printSlice(b, "part3 b")
fmt.Printf("n")
}
func printSlice(x []int, y string) {
fmt.Printf("%v len=%d cap=%d slice=%vn", y, len(x), cap(x), x)
}
在 part3 部分,通过 a[0] 修改 a[0] 值,b[0] 值也会跟着修改;通过 a=append(a, 5) 可以给 a 增加数据项,但
1.13 nil切片和空切片
make([]int,0)
与 var a []int
建的切片是有区别的,前者的切片指针有分配,后者的内部指针为nil。
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
var a []int
b := make([]int, 0)
if a == nil {
// a is nil
fmt.Println("a is nil")
} else {
fmt.Println("a is not nil")
}
//虽然b的底层数组大小为0,但切片并不是nil
if b == nil {
fmt.Println("b is nil")
} else {
// b is not nil
fmt.Println("b is not nil")
}
//使用反射中的SliceHeader来获取切片运行时的数据结构
as := (*reflect.SliceHeader)(unsafe.Pointer(&a))
bs := (*reflect.SliceHeader)(unsafe.Pointer(&b))
// len=0,cap=0,type=0
fmt.Printf("len=%d,cap=%d,type=%dn", len(a), cap(a), as.Data)
// len=0,cap=0,type=824634810008
fmt.Printf("len=%d,cap=%d,type=%dn", len(b), cap(b), bs.Data)
}
1.14 多个切片引用同一个底层数组引发的混乱
切片可以由数组创建,一个底层数组可以创建多个切片,这些切片共享底层数组,使用append 扩展切片过程中可
能修改底层数组的元素,间接地影响其他切片的值,也可能发生数组复制重建,共用底层数组的切片,由于其行为
不明朗,不推荐使用。
多个切片共享一个底层数组,其中一个切片的 append 操作可能引发如下两种情况。
(1)、append追加的元素没有超过底层数组的容量,此种 append 操作会直接操作共享的底层数组,如果其他切
片有引用数组被覆盖的元素,则会导致其他切片的值也隐式地发生变化。
(2)、append追加的元素加上原来的元素如果超出底层数组的容 ,则此种 append 操作会重新申请新数组,并将
原来数组值复制到新数组。
由于有这种二义性,所以在使用切片的过程中应该尽量避免多个切面共享底层数组, 可以使用copy进行显式的复
制。
package main
import (
"fmt"
"reflect"
"unsafe"
)
func main() {
a := []int{0, 1, 2, 3, 4, 5, 6}
b := a[0:4]
as := (*reflect.SliceHeader)(unsafe.Pointer(&a))
bs := (*reflect.SliceHeader)(unsafe.Pointer(&b))
//a、b共享底层数组
// a=[0 1 2 3 4 5 6],len=7,cap=7,type=824633803328
fmt.Printf("a=%v,len=%d,cap=%d,type=%dn", a, len(a), cap(a), as.Data)
// b=[0 1 2 3],len=4,cap=7,type=824633803328
fmt.Printf("b=%v,len=%d,cap=%d,type=%dn", b, len(b), cap(b), bs.Data)
b = append(b, 10, 11, 12)
//a、b继续共享底层数组,修改b会影响共享的底层数组,间接影响a
// a=[0 1 2 3 10 11 12],len=7,cap=7
fmt.Printf("a=%v,len=%d,cap=%dn", a, len(a), cap(a))
// b=[0 1 2 3 10 11 12],len=7,cap=7
fmt.Printf("b=%v,len=%d,cap=%dn", b, len(b), cap(b))
//len(b)=7,底层数组容量是7,此时需要重新分配数组,并将原来数组值复制到新数组
b = append(b, 13, 14)
as = (*reflect.SliceHeader)(unsafe.Pointer(&a))
bs = (*reflect.SliceHeader)(unsafe.Pointer(&b))
//可以看到a和b指向底层数组的指针已经不同了
// a=[0 1 2 3 10 11 12],len=7,cap=7,type=824633803328
fmt.Printf("a=%v,len=%d,cap=%d,type=%dn", a, len(a), cap(a), as.Data)
// b=[0 1 2 3 10 11 12 13 14],len=9,cap=14,type=824633786592
fmt.Printf("b=%v,len=%d,cap=%d,type=%dn", b, len(b), cap(b), bs.Data)
}
原文地址:https://blog.csdn.net/qq_30614345/article/details/131334225
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_22878.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!