golang的切片使用总结二

如果没看golang切片的第一篇总结博客 golang的切片使用总结一-CSDN博客 ,请浏览之

举例9:make([]int, a, b)后访问下标a的元素

s := make([]int, 10, 12)
v := s[10]
fmt.Printf("v:%v", v)

打印结果:

panic: runtime error: index out of range [10] with length 10

goroutine 1 [running]:
main.main()

结论:capacity(容量)是物理意义上的,空间归切片s所有;但len(长度)是逻辑意义上的,访问元素时是根据逻辑意义为准,因为s[10]认为是越界访问

举例10:make([]int, a, b)后截取新切片,再对新切片append

s := make([]int, 10, 12)
s1 := s[8:]
s1 = append(s1, []int{10, 11, 12}...)
v := s[10]
fmt.Printf("v:%v", v)

打印结果:

panic: runtime error: index out of range [10] with length 10

goroutine 1 [running]:
main.main()
结论:虽然s1从s截取得到,二者共享同一块内存数据。但是后面的s1 = append(s1)操作会让s1发生扩容,s1扩容后就跟s完全分开了,内存完全独立。所以,s还是原来的len为10,访问s[10]会发生panic

举例11:切片在函数中是值传递还是引用传递

func main() {
    s := make([]int, 10, 12)
    s1 := s[8:]
    changeSlice(s1)
    fmt.Printf("s: %v", s)
}

func changeSlice(s1 []int) {
    s1[0] = -1
}

打印结果:s: [0 0 0 0 0 0 0 0 -1 0]

结论:切片s1是从切片s截取得到,传入函数后,由于切片是引用传递,函数内的s1[0]和函数外的s[8]是同一个元素,所以原切片s会被修改

举例12:切片传递到函数内后进行修改,且append

func main() {
    s := make([]int, 10, 12)
    s1 := s[8:]
    changeSlice(s1)
    fmt.Printf("s:%v,  len of s:%v,  cap of s:%v  \n", s, len(s), cap(s))
    fmt.Printf("changeSlice函数后, s1:%v, len of s1:%v, cap of s1:%v \n", s1, len(s1), cap(s1))
}
func changeSlice(s1 []int) {
    s1[0] = -1
    s1 = append(s1, 10, 11, 12, 13, 14, 15)
    fmt.Printf("changeSlice函数内, s1:%v, len of s1:%v, cap of s1:%v \n", s1, len(s1), cap(s1))
}

打印结果

changeSlice函数内, s1:[-1 0 10 11 12 13 14 15], len of s1:8, cap of s1:8 
s:[0 0 0 0 0 0 0 0 -1 0],  len of s:10,  cap of s:12
changeSlice函数后, s1:[-1 0], len of s1:2, cap of s1:4

结论:虽然切片是引用传递,实际指的是元素数据存储为引用,但切片参数仍然是不同的slice header。有点儿像C++的指针,两个指针指向的数据是同一份地址,但是两个指针本身是不同的。

所以函数changeSlice()内的s1,函数外的s1,旧切片s,三者指向的是同一块数据,一处修改即生效。但是函数changeSlice()内的s1,函数外的s1代表的是两个不同的slice header,函数执行只是修改函数内s1的slice header,函数外面s1的slice header不受影响,长度仍然是2,capacity仍然是4

举例13:多次截取切片后赋值

s := []int{0, 1, 2, 3, 4}
s = append(s[:2], s[3:]...)
fmt.Printf("s:%v, len(s)=%v, cap(s)=%v \n", s, len(s), cap(s))
v := s[4]
fmt.Printf("v=%v", v)

打印结果:

s:[0 1 3 4], len(s)=4, cap(s)=5 
panic: runtime error: index out of range [4] with length 4

goroutine 1 [running]:
main.main()
结论:执行append(s[:2],s[3:]...)后,s中有4个元素,capacity仍然为5,使用下标访问s时使用的是逻辑长度,认为是越界

举例14:切片超过256时,扩容时的公式

s := make([]int, 512)
s = append(s, 1)
fmt.Printf("len(s)=%v,cap(s)=%v", len(s), cap(s))

打印结果:len(s)=513,cap(s)=848

结论:切片中元素超过512时,扩容公式不是直接翻倍,而是每次递增(N/4 + 192),直到值达到需求,其中的192=(3*256)/4

按照上面的公式,512 +(512/4+192) = 832个元素

但是为什么这里容量显示是848呢?这关联到golang的内存对齐

为了更好地进行内存空间对齐,golang 允许产生一些有限的内部碎片,对拟申请空间的 object 进行大小补齐,最终 6656 byte 会被补齐到 6784 byte 的这一档次,各个档次表如下所示:

// class  bytes/obj  bytes/span  objects  tail waste  max waste  min align
//     1          8        8192     1024           0     87.50%          8
//     2         16        8192      512           0     43.75%         16
//     3         24        8192      341           8     29.24%          
// ...
//    48       6528       32768        5         128      6.23%        128
//    49       6784       40960        6         256      4.36%        128

刚才计算出来的832元素,每个int占8个字节,所以832 * 8字节 = 6656字节

所以我们需要6656字节时,根据上面表格,落在6784这一档,golang帮我们申请了6784个字节,

6784字节 / 8字节 = 848个int元素

最终计算得到capacity为848

本篇总结:

1. 切片的capacity可以认为是物理意义上的空间;而len是逻辑意义上的元素个数

2. 根据下标访问切片时,golang的执行的是逻辑判断,不能大于或等于len的值,否则会认为是越界,发生panic

3. 切片在函数参数中传递时是引用传递,但这里的引用指的是存储的数据指向同一份。但函数内外的参数仍然是不同的slice header,就像两个指针一样

4. 切片元素超过256时,切片扩容不再是简单的翻倍,而是有个递增公式,每次增加为N/4+192。但golang申请内存时还有内存对齐的问题,有个档次表。申请内存时,在哪个档则采用这个档的值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Golang的数组和切片是两种不同的数据类型,用于存储相同数据类型的容器。数组的长度是固定的,而切片的长度是可变的。在日常应用中,切片使用更为普遍。 数组在声明时需要指定长度,并且在初始化时必须提供相同长度的元素。例如,`a := int{1, 2, 3}`就是一个长度为3的整数数组。数组的长度一旦确定后就不能更改。 切片是基于数组的引用类型。它不需要指定固定的长度,并且可以根据需要动态扩展或缩小。切片包装着底层数组,通过指定起始索引和结束索引来指定子集。例如,`b := a[:]`就是一个切片,它包含了数组a的所有元素。 数组适用于需要固定长度的场景,而切片适用于长度可变的情况。在实际应用中,切片更常用,因为它提供了更大的灵活性和便利性。 总结: - 数组是长度固定的容器,切片是长度可变的容器; - 数组在声明时需要指定长度,切片则不需要; - 数组的长度一旦确定后就不能更改,而切片可以根据需要动态扩展或缩小; - 切片是基于数组的引用类型,可以通过指定起始索引和结束索引来指定子集。 参考资料: Golang中的「数组」和「切片」都是存储同一数据类型的容器,只不过Golang中的数组长度是固定的,而切片的长度是可变化的。 切片是引用类型,切片包装的数组称为该切片的底层数组。我们来看一段代码://a是一个数组,注意数组是一个固定长度的,初始化时候必须要指定长度,不指定长度的话就是切片了 a := int{1, 2, 3} //b是数组,是a...。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值