赞
踩
Go 语言切片是对数组的抽象。
Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切
片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。
你可以声明一个未指定大小的数组来定义切片:
var identifier []type
切片不需要说明长度。
或使用 make() 函数来创建切片:
var slice1 []type = make([]type, len)
// 也可以简写为
slice1 := make([]type, len)
也可以指定容量,其中 capacity 为可选参数。
make([]T, length, capacity)
这里 len 是数组的长度并且也是切片的初始长度。
s :=[] int {1,2,3 }
直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。
s := arr[:]
初始化切片 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 初始化切片 s1。
s :=make([]int,len,cap)
通过内置函数 make() 初始化切片 s,[]int 标识为其元素类型为 int 的切片。
切片是可索引的,并且可以由 len() 方法获取长度。
切片提供了计算容量的方法 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=%v\n", len(x), cap(x), x)
}
一个切片在未初始化之前默认为 nil,长度为 0。
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=%v\n", len(x), cap(x), x) }
可以通过设置下限及上限来设置截取切片 [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=%v\n", len(x), cap(x), x) }
我们可以看出切片,实际的是获取数组的某一部分,len切片<=cap切片<=len数组,切片由三部分组成:指向底
层数组的指针、len、cap。
如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。
下面的代码描述了从拷贝切片的 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=%v\n", 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: %v\n", 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=%v\n", 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=%v\n", len(x), cap(x), x) }
我们基于原数组或者切片创建一个新的切片后,那么新的切片的大小和容量是多少呢?
这里有个公式,对于底层数组容量是 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("%d\n", numbers[1:3]) // [2 3 4 5 6] fmt.Printf("%d\n", numbers[2:7]) // [0 1 2] fmt.Printf("%d\n", numbers[:3]) // [4 5 6 7 8 9 10] fmt.Printf("%d\n", 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=%v\n", len(x), cap(x), x) }
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的计算:
1、当同时添加多个元素时:
len(list)+len([params])为偶数: cap=len(list)+len([params])
len(list)+len([params])为奇数: cap=len(list)+len([params])+1
即 cap 始终为偶数。
2、当一个一个添加元素时:
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)的两倍,即
(
newcap=doublecap
)。3、否则,如果旧切片长度大于等于阈值,则最终容量(newcap)从旧容量(old.cap)开始循环增加为原来的1.25
倍,即
newcap += (newcap + 3*threshold) / 4
,直到最终容量(newcap)大于等于新申请的容量(cap),即(
newcap >= cap
)。4、如果最终容量(cap)计算值溢出,则最终容量(cap)就是新申请容量(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=%v\n", len(x), cap(x), x) }
在做函数调用时,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 }
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,%#v\n", 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,%#v\n", 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,%#v\n", 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,%#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)) }
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=%v\n", len(x), cap(x), x) }
做 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))
}
修改 max 值,发生越界错误:
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]:
&会让原切片发生改变,切片也会让原切片发生改变,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=%v\n", 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的地址:%p\n", a) // 切片x的地址:0xc00000e1a0 fmt.Printf("切片x的地址:%p\n", 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的地址:%p\n", b) // 切片y的地址:0xc00000e1e0 fmt.Printf("切片y的地址:%p\n", 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]
的值,没有发生脱钩;要想使两个切片同步改变,最好的
方式是使用切片指针来实现,也就是上面的 *d
。
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=%v\n", 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=%v\n", y, len(x), cap(x), x) }
修改后的代码中,将 a, b 的 cap 都设置成 10;
在 part3 部分,通过 a[0] 修改 a[0] 值,b[0] 值也会跟着修改;通过 a=append(a, 5) 可以给 a 增加数据项,但
是 b 的数据项并没有增加。
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=%d\n", len(a), cap(a), as.Data) // len=0,cap=0,type=824634810008 fmt.Printf("len=%d,cap=%d,type=%d\n", len(b), cap(b), bs.Data) }
切片可以由数组创建,一个底层数组可以创建多个切片,这些切片共享底层数组,使用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=%d\n", 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=%d\n", 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=%d\n", a, len(a), cap(a)) // b=[0 1 2 3 10 11 12],len=7,cap=7 fmt.Printf("b=%v,len=%d,cap=%d\n", 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=%d\n", 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=%d\n", b, len(b), cap(b), bs.Data) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。