Go语言切片Slice

news/2025/2/13 6:25:56/

1、Go语言切片Slice

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切

片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

1.1 定义切片

你可以声明一个未指定大小的数组来定义切片:

var identifier []type

切片不需要说明长度。

或使用 make() 函数来创建切片:

var slice1 []type = make([]type, len)
// 也可以简写为
slice1 := make([]type, len)

也可以指定容量,其中 capacity 为可选参数。

make([]T, length, capacity)

这里 len 是数组的长度并且也是切片的初始长度。

1.2 切片初始化

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 的切片。

1.3 len() 和 cap() 函数

切片是可索引的,并且可以由 len() 方法获取长度。

切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。

package mainimport "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)
}

1.4 空(nil)切片

一个切片在未初始化之前默认为 nil,长度为 0。

package mainimport "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)
}

1.5 切片截取

可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound]。

package mainimport "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。

1.6 append() 和 copy() 函数

如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。

下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。

package mainimport "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=6printSlice(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 mainimport "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 mainimport "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 mainimport "fmt"func main() {// 令size=3dst := 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)
}

1.7 切片截取的长度问题

我们基于原数组或者切片创建一个新的切片后,那么新的切片的大小和容量是多少呢?

这里有个公式,对于底层数组容量是 k 的切片 slice[i:j] 来说:

长度: j-i
容量: k-i

实例:

package mainimport "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=11printSlice(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=9printSlice(number3)number4 := numbers[3:8]// len=5  cap=8   slice=[3 4 5 6 7]// cap=11-3=8printSlice(number4)
}func printSlice(x []int) {fmt.Printf("len=%d  cap=%d   slice=%v\n", len(x), cap(x), x)
}

1.8 append函数长度问题

package mainimport "fmt"func main() {numbers := []int{0, 1}// numbers的容量:  2fmt.Println("numbers的容量: ", cap(numbers))// numbers的长度:  2fmt.Println("numbers的长度: ", len(numbers))numbers = append(numbers, 2, 3, 4)// numbers的容量:  6fmt.Println("numbers的容量: ", cap(numbers))// numbers的长度:  5fmt.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 = 256if 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 mainimport "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)
}

1.9 切片是引用传值

在做函数调用时,slice 按引用传递,array 按值传递。

package mainimport "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 datauintgo    len;        // number of elementsuintgo    cap;        // allocated number of elements};

第一个字段表示 array 的指针,是真实数据的指针。第二个是表示 slice 的长度,第三个是表示 slice 的容量。

所以 unsafe.Sizeof(切片) 永远都是 24。

当把 slice 作为参数,本身传递的是值,但其内容就 byte* array,实际传递的是引用,所以可以在函数内部修

改。但如果对 slice 本身做 append,而且导致 slice 进行了扩容,实际扩容的是函数内复制的一份切片,对于函数

外面的切片没有变化。

package mainimport ("fmt""unsafe"
)func main() {slice_test := []int{1, 2, 3, 4, 5}// 24fmt.Println(unsafe.Sizeof(slice_test))// main:[]int{1, 2, 3, 4, 5},5,5fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))// slice_value:[]int{1, 100, 3, 4, 5, 6},6,10slice_value(slice_test)// main:[]int{1, 100, 3, 4, 5},5,5fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))// slice_ptr:[]int{1, 100, 3, 4, 5, 7},6,10slice_ptr(&slice_test)// main:[]int{1, 100, 3, 4, 5, 7},6,10fmt.Printf("main:%#v,%#v,%#v\n", slice_test, len(slice_test), cap(slice_test))// 24fmt.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 mainimport "fmt"func main() {// len=3, cap=3s := []int{1, 2, 3}a := ss[0] = 888// [888 2 3] 3 3fmt.Println(a, len(a), cap(a))// [888 2 3] 3 3fmt.Println(s, len(s), cap(s))// append添加的元素对a不生效s = append(s, 4)// [888 2 3 4] 4 6fmt.Println(s, len(s), cap(s))// [888 2 3] 3 3fmt.Println(a, len(a), cap(a))
}

实例2:

package mainimport "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)
}

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 mainimport "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)// 8fmt.Println(len(s))// 9fmt.Println(cap(s))
}

修改 max 值,发生越界错误:

package mainimport "fmt"func main() {s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}// 修改 max 值为 13s = 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 10goroutine 1 [running]:

1.12 append会让切片和与它相关的切片脱钩,但地址不变

&会让原切片发生改变,切片也会让原切片发生改变,append不会发生改变:

package mainimport ("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 := ad := &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 mainimport "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的地址:0xc00000e1a0fmt.Printf("切片a的地址:%p\n", a)// 切片x的地址:0xc00000e1a0fmt.Printf("切片x的地址:%p\n", x)fmt.Println()y := make([]int, 4)b := yb[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的地址:0xc000014240fmt.Printf("切片b的地址:%p\n", b)// 切片y的地址:0xc00000e1e0fmt.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 mainimport ("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 mainimport ("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] = 1a[1] = 2a[2] = 3a[3] = 4b := 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 的数据项并没有增加。

1.13 nil切片和空切片

make([]int,0)var a []int建的切片是有区别的,前者的切片指针有分配,后者的内部指针为nil。

package mainimport ("fmt""reflect""unsafe"
)func main() {var a []intb := make([]int, 0)if a == nil {// a is nilfmt.Println("a is nil")} else {fmt.Println("a is not nil")}//虽然b的底层数组大小为0,但切片并不是nilif b == nil {fmt.Println("b is nil")} else {// b is not nilfmt.Println("b is not nil")}//使用反射中的SliceHeader来获取切片运行时的数据结构as := (*reflect.SliceHeader)(unsafe.Pointer(&a))bs := (*reflect.SliceHeader)(unsafe.Pointer(&b))// len=0,cap=0,type=0fmt.Printf("len=%d,cap=%d,type=%d\n", len(a), cap(a), as.Data)// len=0,cap=0,type=824634810008fmt.Printf("len=%d,cap=%d,type=%d\n", len(b), cap(b), bs.Data)
}

1.14 多个切片引用同一个底层数组引发的混乱

切片可以由数组创建,一个底层数组可以创建多个切片,这些切片共享底层数组,使用append 扩展切片过程中可

能修改底层数组的元素,间接地影响其他切片的值,也可能发生数组复制重建,共用底层数组的切片,由于其行为

不明朗,不推荐使用。

多个切片共享一个底层数组,其中一个切片的 append 操作可能引发如下两种情况。

(1)、append追加的元素没有超过底层数组的容量,此种 append 操作会直接操作共享的底层数组,如果其他切

片有引用数组被覆盖的元素,则会导致其他切片的值也隐式地发生变化。

(2)、append追加的元素加上原来的元素如果超出底层数组的容 ,则此种 append 操作会重新申请新数组,并将

原来数组值复制到新数组。

由于有这种二义性,所以在使用切片的过程中应该尽量避免多个切面共享底层数组, 可以使用copy进行显式的复

制。

package mainimport ("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=824633803328fmt.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=824633803328fmt.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=7fmt.Printf("a=%v,len=%d,cap=%d\n", a, len(a), cap(a))// b=[0 1 2 3 10 11 12],len=7,cap=7fmt.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=824633803328fmt.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=824633786592fmt.Printf("b=%v,len=%d,cap=%d,type=%d\n", b, len(b), cap(b), bs.Data)
}

http://www.ppmy.cn/news/504901.html

相关文章

【计算机网络自顶向下】简答题习题总结(二)

目录 第二章 应用层 HTTP FTP文件传输协议 电子邮件 域名系统DNS 内容分发网络CDN 常考问题 题目 第二章 应用层 HTTP 网页&#xff08;Web页&#xff0c;或称文档&#xff09;由许多对象组成&#xff0c;每个对象被一个URL(Uniform Resource Locator统一资源定位符)寻…

建立小型医学数据库(总结)

建立小型医学数据库 小型医学数据库可以用于存储和管理医学数据&#xff0c;如患者病历、药品信息、试验结果等。这对于医疗机构和科研机构来说非常必要&#xff0c;可以提高数据管理和共享的效率&#xff0c;进而促进医学研究和诊疗水平的提升。 建立小型医学数据库有以下基本…

MSP432学习笔记10:串口接收字符串命令并执行任务

今日终于得以继续我的MSP432电赛速通之路&#xff1a; 串口通信是单片机需要学习深入的一个很重要的板块&#xff0c;通过串口&#xff0c;我们可以实现许多数据收发与调试工作&#xff0c;可谓是非常方便快捷。 今日就跟随我的脚步&#xff0c;逐步扎实地学习如何编程MSP432…

mac qq电话时其他媒体声音变小

Terminal printf "p (char)(void(*)())AudioDeviceDuck0xc3\nq" | lldb -n QQ 但是每次用都需要一次&#xff0c;或者下个程序手动调一下 sound control

Dell Inspiron 15R - QQ语音时麦克风没有声音的设置办法

右键扬声器/耳机&#xff08;任务栏的小喇叭图标&#xff09;-》播放设备&#xff08;P)&#xff0c;禁用通讯耳机&#xff1b; 右键扬声器/耳机&#xff08;任务栏的小喇叭图标&#xff09;-》录音设备&#xff08;R)&#xff0c;禁用麦克风/输入端&#xff1b; 对于QQ语音的…

电脑版微信,QQ语音通话耳机听不到对方声音

这是开组会汇报时&#xff0c;发现老师能听到我的声音&#xff0c;我的蓝牙耳机听不到他的声音发现的问题 原以为是系统问题或者是蓝牙耳机问题&#xff0c;结果发现是我的问题 解决方案&#xff1a;在打开微信通话或者QQ通话、分享屏幕前&#xff0c;选择右下角声音&#xff…

直播app与微信qq同时占用麦克风,或者播放音乐时来电,解决麦克风冲突

//来电状态监听public class PhoneVVStateListener {// 电话管理器private TelephonyManager tm;// 监听器对象private MyListener listener;private AudioManager ams null;//音频管理器public void start() {//开始监听tm (TelephonyManager) context.getSystemService(TEL…

mac QQ 语音或视频时其他声音变小的解决办法

在使用MacBook的时候&#xff0c;使用QQ视频的同时 看视频 听歌都会发现&#xff0c;QQ视频声音正常&#xff0c;其他软件的声音会很小很小。怎么解决呢&#xff0c;首先 开启QQ后&#xff0c;在视频之前&#xff1a; 1、打开终端输入以下命令。 printf "p *(char*)(void(…