前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于Golang切片Slice和append的有趣问题

关于Golang切片Slice和append的有趣问题

作者头像
benny
发布2021-09-07 16:22:21
1.1K0
发布2021-09-07 16:22:21
举报
文章被收录于专栏:程序员的碎碎念

开局一道题

请大家猜猜打印x和y的内容会是什么?以及想想为什么会这样子?其中的知识点有哪些?

代码语言:javascript
复制
package main

import (
    "fmt"
)

func main() {
    x := []int{1, 2, 3}
    y := x[:2]    
    y = append(y, 10)
    fmt.Println("x=", x, &x[0])
    fmt.Println("y=", y, &y[0])
    y = append(y, 20)
    fmt.Println("y=", y, &y[0])
    y[0] = 20
    for i := 0; i < 10; i++ {
	y = append(y, i)
    }
    fmt.Println("y=", y, &y[0])
}
答案
代码语言:javascript
复制
package main

import (
    "fmt"
)

func main() {
    x := []int{1, 2, 3}
    y := x[:2]//【1】
    y = append(y, 10)//【2】
    fmt.Println("x=", x, &x[0])    //x= [1 2 10] 0xc0000b6020
    fmt.Println("y=", y, &y[0])    //y= [1 2 10] 0xc0000b6020
    y = append(y, 20)//【3】
    fmt.Println("y=", y, &y[0])    //y= [1 2 10 20] 0xc0000ac030
    y[0] = 20//【4】
    for i := 0; i < 10; i++ {
    	y = append(y, i)
    }
    fmt.Println("y=", y, &y[0])    //y= [20 2 10 20 0 1 2 3 4 5 6 7 8 9] 0xc0000ba000 【5】
}

解释

  • 【1】因为y是x的slice切片{1,2},所以y和x指向的内存地址是一样的;
  • 【2】因为y指向的内存地址和x是一样的,在尾部append一个值的时候,会挤掉后面的值3,故这时候x和y都为1,2,10
  • 【3】这时候y又再次appned,超出了原来的大小3,这时候会会分配一个更大数组来容纳,会新建一块独立的内存地址给到y(y独立了,和x没有什么关系了)。故y为1,2,10,20,x还是为1,2,10
  • 【4】由于y已指向全新的内存地址,改变下标为0的值为10,则y为20,2,10,20
  • 【5】slice扩容,新开辟一块更大内存,把之前的数据复制过去,则y指向地址变化了

知识点

Slice实现原理
代码语言:javascript
复制
}

slice 的数据结构,一个指向真实 array 地址的指针 ptr ,slice 的长度 len 和容量 cap ,在底层数组容量不足时可以实现自动重分配并生成新的Slice,在实际使用中,我们最好事先预期好一个cap,这样在使用append的时候可以避免反复重新分配内存复制之前的数据,减少不必要的性能消耗。

举例
代码语言:javascript
复制
data = append(data, i)
结果
代码语言:javascript
复制
data= [0] 0xc0000b4008 //【1】初始化地址
data= [0 1] 0xc0000b4030    //【2】第一次扩容+1,地址改变
i=%d,data=%v
 0 0xc0000b6040             //【3】第二次扩容+2,地址改变
i=%d,data=%v
 1 0xc0000b6040             //Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
i=%d,data=%v
 2 0xc0000ba000             //【4】第三次扩容+4,地址改变
i=%d,data=%v
 3 0xc0000ba000             //Slice容量够用,则将新元素追加进去,Slice.len++,返回原Slice
i=%d,data=%v
 4 0xc0000ba000             //同上
i=%d,data=%v
 5 0xc0000ba000             //同上
i=%d,data=%v
 6 0xc0000bc000             //【5】第四次扩容+8,地址改变   
i=%d,data=%v
 7 0xc0000bc000             //同上
i=%d,data=%v
 8 0xc0000bc000             //同上
i=%d,data=%v
 9 0xc0000bc000             //同上

扩容容量的选择遵循以下规则:

如果原Slice容量小于1024,则新Slice容量将扩大为原来的2倍 如果原Slice容量大于等于1024,则新Slice容量将扩大为原来的1.25倍

总结

创建切片时可根据实际需要预分配容量,尽量避免追加过程中扩容操作(append),有利于提升性能

参考

Golang语言slice实现原理及使用方法

golang slice 切片原理

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-08-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员的碎碎念 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开局一道题
    • 答案
    • 解释
    • 知识点
      • Slice实现原理
        • 举例
          • 结果
            • 总结
            • 参考
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档