当前位置: 首页 > news >正文

切片

在 Go 语言中,切片(Slice) 是数组的「动态视图」,也是开发中最常用的复合数据类型。它解决了数组「长度固定」的痛点,支持动态扩容,且本质是对底层数组的引用,兼顾灵活性和性能。

一、切片的核心本质

切片不是独立的存储结构,而是一个包含 3 个字段的引用类型结构体

type slice struct {ptr *[]T   // 指向底层数组的指针len int    // 切片当前可用元素的长度(可访问的索引范围:0 ~ len-1)cap int    // 切片的容量(底层数组从指针位置开始的总长度,cap >= len)
}

简单理解:切片 = 「数组指针」+「当前长度」+「最大容量」。

二、切片的声明与初始化

1. 基本声明(未初始化,值为 nil)

// 格式:var 切片名 []元素类型
var s1 []int       // nil 切片,len=0,cap=0
var s2 []string    // nil 切片,len=0,cap=0
fmt.Println(s1 == nil) // true

2. 常用初始化方式

方式1:从数组/已有切片截取(最核心)

格式:切片 = 原数组/切片[起始索引:结束索引](左闭右开,包含起始,不包含结束)。

// 1. 从数组截取
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[1:4] // 截取索引1~3,结果 [2,3,4],len=3,cap=4(底层数组从索引1到末尾共4个元素)
s2 := arr[:3]  // 省略起始索引,默认从0开始,结果 [1,2,3],len=3,cap=5
s3 := arr[2:]  // 省略结束索引,默认到数组末尾,结果 [3,4,5],len=3,cap=3
s4 := arr[:]   // 截取全部,结果 [1,2,3,4,5],len=5,cap=5// 2. 从切片截取
s5 := s1[1:2]  // 从s1截取索引1~1,结果 [3],len=1,cap=3(继承s1的cap)
方式2:字面量初始化(直接创建)
// 格式:切片名 := []元素类型{元素1, 元素2, ...}
s1 := []int{1, 2, 3}       // len=3,cap=3(底层自动创建长度为3的数组)
s2 := []string{"a", "b"}   // len=2,cap=2
方式3:make 函数初始化(指定长度/容量,推荐)

适用于提前知道切片大致大小,避免频繁扩容,格式:make([]T, len, cap)(cap 可选,默认等于 len)。

// 格式1:只指定长度(cap = len)
s1 := make([]int, 3)       // len=3,cap=3,元素默认值0 → [0,0,0]// 格式2:指定长度和容量(cap >= len)
s2 := make([]int, 3, 5)    // len=3,cap=5,前3个元素为0,后2个为底层数组预留空间

三、切片的核心操作

1. 访问/修改元素

与数组一致,通过索引访问,修改切片元素会同步修改底层数组(因为是引用):

package main
import "fmt"func main() {arr := [5]int{1,2,3,4,5}s := arr[1:4] // [2,3,4]// 访问元素fmt.Println(s[0]) // 2// 修改切片元素 → 底层数组也会变s[1] = 300fmt.Println(s)    // [2,300,4]fmt.Println(arr)  // [1,2,300,4,5](原数组被修改)
}

2. 遍历切片

与数组完全一致,推荐用 for-range

s := []string{"Go", "Python", "Java"}// 方式1:下标遍历
for i := 0; i < len(s); i++ {fmt.Println(i, s[i])
}// 方式2:for-range(推荐)
for i, v := range s {fmt.Printf("索引%d:%s\n", i, v)
}// 忽略索引
for _, v := range s {fmt.Println(v)
}

3. 动态扩容(append 函数)

append 是切片动态扩容的核心函数,格式:新切片 = append(原切片, 元素1, 元素2, ...)

  • len < cap:直接在底层数组空闲位置添加元素,len+1,cap 不变;
  • len == cap:自动扩容(通常扩容为原 cap 的 2 倍,小切片可能扩更多),创建新底层数组,拷贝原元素,再添加新元素。
s := make([]int, 3, 5) // len=3, cap=5, [0,0,0]
s = append(s, 1)       // len=4, cap=5, [0,0,0,1]
s = append(s, 2, 3)    // len=6, cap=10(扩容), [0,0,0,1,2,3]// 切片拼接(第二个切片后加 ...)
s1 := []int{1,2}
s2 := []int{3,4}
s3 := append(s1, s2...) // [1,2,3,4]

⚠️ 注意:append 可能返回新切片(扩容时),因此必须接收返回值,否则原切片可能失效。

4. 切片的拷贝(copy 函数)

copy 用于将一个切片的元素拷贝到另一个切片,格式:拷贝的元素数 = copy(目标切片, 源切片)

  • 拷贝数量取两个切片长度的较小值;
  • 拷贝是值拷贝,修改目标切片不会影响源切片。
s1 := []int{1,2,3,4}
s2 := make([]int, 2)   // [0,0]n := copy(s2, s1)      // 拷贝2个元素,s2 = [1,2],n=2
fmt.Println(s2, n)// 完整拷贝(目标切片长度 >= 源切片)
s3 := make([]int, len(s1))
copy(s3, s1) // s3 = [1,2,3,4]

5. 切片作为函数参数

切片是引用类型,传参时仅拷贝「切片头」(指针、len、cap,共 24 字节),函数内修改切片元素会影响原切片:

func modifySlice(s []int) {s[0] = 100        // 修改底层数组元素s = append(s, 4)  // 若扩容,s指向新数组,原切片不受影响
}func main() {s := []int{1,2,3}modifySlice(s)fmt.Println(s)    // [100,2,3](元素被修改,但append的4未体现,因为扩容后函数内s是新切片)
}

四、切片的常见坑(新手必避)

  1. nil 切片 vs 空切片

    • nil 切片:var s []int → ptr=nil,len=0,cap=0;
    • 空切片:s := []int{}s := make([]int, 0) → ptr≠nil(指向空数组),len=0,cap=0;
    • 二者均可正常使用 append/len/cap,判断切片是否为空应使用 len(s) == 0,而非 s == nil
  2. 切片共享底层数组
    多个切片截取同一数组时,修改其中一个切片的元素会影响其他切片:

    arr := [5]int{1,2,3,4,5}
    s1 := arr[1:3] // [2,3]
    s2 := arr[2:4] // [3,4]
    s1[1] = 300    // s1=[2,300], s2=[300,4], arr=[1,2,300,4,5]
    
  3. 扩容后切片解耦
    切片扩容后会指向新数组,原切片与新切片不再共享底层数组:

    s1 := []int{1,2} // len=2, cap=2
    s2 := s1
    s1 = append(s1, 3) // 扩容,s1指向新数组 [1,2,3]
    s2[0] = 100        // s2仍指向原数组 [100,2]
    fmt.Println(s1)    // [1,2,3]
    fmt.Println(s2)    // [100,2]
    

五、切片 vs 数组(核心对比)

特性 数组(Array) 切片(Slice)
类型标识 [N]T(长度是类型的一部分) []T(无长度)
长度 固定 动态(len 可通过 append 改变)
容量 等于长度 独立于长度(cap >= len)
底层 直接存储元素 引用底层数组
传参 值拷贝(拷贝整个数组) 引用传递(拷贝切片头,开销小)
初始化 可直接声明 需字面量/make/截取

总结

  1. Go 切片是引用类型,核心结构为「指针+长度+容量」,底层依赖数组,支持动态扩容;
  2. 切片的核心操作:append(扩容)、copy(拷贝)、截取([start:end]),append 必须接收返回值;
  3. 切片传参是引用传递(修改元素影响原切片),但扩容会生成新切片,需注意共享底层数组的副作用;
  4. 判断切片是否为空用 len(s) == 0,而非 s == nil,实际开发中切片几乎完全替代数组。
http://www.jsqmd.com/news/522151/

相关文章:

  • 了解2026年东光优质锅炉制造厂家分析,选锅炉不迷路,蒸汽锅炉/锅炉/导热油锅炉,锅炉销售厂家分析 - 品牌推荐师
  • 从商宇UPS到微模块数据中心,看四川骏杨明如何定义2026机房基础设施价值 - 速递信息
  • 预算有限怎么租最划算?2026年四川地区彩色复印机、会议设备租赁性价比排名 - 速递信息
  • 2026年常州稳定型网眼袋切缝机费用多少,哪家值得选 - 工业设备
  • 如何选择防辐射工程方案?2026四川成都医用铅门铅玻璃施工厂家排名解析 - 速递信息
  • 2026年制袋机口碑排行榜,含节能改造、来样定制与半自动款式 - 工业品牌热点
  • 2026 年贵州波纹管优质实力厂家盘点 靠谱可靠口碑优选 - 深度智识库
  • SAP-ABAP-SLAV用法
  • 从零实现富文本编辑器#12-React可编辑节点的组件预设
  • 2026 年贵州 PE 管优质实力厂家盘点 靠谱可靠品牌选购指南 - 深度智识库
  • 立足成都,服务四川:2026年辐射防护铅门、铅玻璃、硫酸钡板实力厂家口碑盘点与推荐 - 速递信息
  • 2026年GEO优化服务商深度技术测评:五家厂商全链路解决方案拆解 - 品牌推荐
  • 常州编织袋自动切缝机口碑好的厂家有哪些 - myqiye
  • 2026中小企业CRM深度横评:从客户管理到外勤管理的全维度对决 - jfjfkk-
  • 聊聊2026年佛山口碑好的蜂鸣器厂家,靠谱之选怎么选择 - 工业品网
  • 2026年给袋式包装机选购攻略,推荐好用的厂商 - 工业品网
  • 回收新手指南:让瑞祥卡变现更实惠 - 团团收购物卡回收
  • 分析广东优质开箱机厂家,哪家品牌靠谱且性价比高? - mypinpai
  • 2026年GEO行业深度观察:AI流量争夺下的五大服务商实力解析 - 品牌推荐
  • 2026年办理分离压力容器制造许可证,好用的公司有哪些 - 工业推荐榜
  • 办公室、学校、企业彩色复印机短期租赁哪家靠谱?2026年四川本地实力供应商排名与选择指南 - 速递信息
  • 分析2026年松万机械设备,服务水平怎样产品精度高不高有答案 - 工业推荐榜
  • 2026年2月国内较优陀螺仪生产厂家推荐及分析,MEMS惯性传感器/惯性导航系统(INS),陀螺仪源头厂家有哪些 - 品牌推荐师
  • 2026年年度排名,乌鲁木齐靠谱的防腐木隔音木屋 - 工业设备
  • 2026客户管理系统选型指南:10款CRM从销售全链路到自动化运营对比 - jfjfkk-
  • 题集:洛谷 P1843 奶牛晒衣服
  • 在 SAP Fiori 开发里理解 Atom 与 JSON:看懂 OData 数据表达方式的取舍逻辑
  • 本地商家别做“无效种草”!3个常见错误,避开立刻提升转化 - Redbook_CD
  • 聊聊2026年吐鲁番防腐木木屋定制,靠谱的厂家怎么选择 - 工业品牌热点
  • 拒绝隐患!贵州PE管怎么选?这6家本地源头厂家闭眼入(附案例) - 深度智识库