Go切片详解

今天我们将讨论 切片(slice) 。它们和数组非常相似,但由于切片更加灵活,在 Go 中我们会更频繁地使用切片而不是数组


切片声明(Slice declaration)

切片的声明方式和数组很像,只是不需要指定大小

go 复制代码
var s []int

由于切片的类型中不包含长度信息 ,你可以给同一个切片变量 s 赋值不同长度的切片:

go 复制代码
s = []int{0, 1, 3}
s = []int{120, 56}
s = []int{43, 42, 12, 12}

和数组一样,你可以使用 [] 来访问或修改切片中的元素:

go 复制代码
s[0] = 20
fmt.Println(s[0]) // 将打印 20

切片与数组的核心区别

数组和切片的核心区别在于:

  • 数组直接保存数据

  • 切片 保存的是指向数组的引用(指针)

就像指针一样,切片的默认值是 nil。当切片未初始化时,也会表现出类似指针的行为。

go 复制代码
var s []int // 声明了一个切片,但没有初始化;
            // s 等于 nil

s[0] = 10   // 会 panic:index out of range [0] with length 0

代码解释

这里的 snil,没有底层数组,因此不能访问任何元素。


初始化切片(Initialization)

如果你需要一个包含大量元素的切片(例如 1000 个元素),该怎么办呢?

这时就需要用到 make 函数

make 函数

make 用于分配并初始化切片(或 map、channel)。

new 不同:

  • new 只接收类型

  • make 接收 长度(len)容量(cap)

make 返回的是 切片本身,而不是切片的指针,因为切片已经是引用类型。

go 复制代码
var s []int
s = make([]int, 6) // 如果不指定容量,cap 默认等于 len

代码解释

  • len:切片中已经存在的元素个数

  • 这些元素都会被初始化为类型的零值

  • cap:在不重新分配底层数组的情况下,切片最多可以扩展到的长度


获取长度和容量

你可以使用内置函数获取切片(或数组)的长度和容量:

go 复制代码
var length = len(s)
var capacity = cap(s)

数组 vs 切片(类比)

数组和切片的关系,有点类似于:

变量 vs 指针

它们的相似点包括:

  • 都需要使用内置函数进行初始化

  • 默认值都是 nil

  • 都不直接存储数据,而是引用数据


创建多维切片(Making a multi-dimensional slice)

make 只会初始化最外层切片

如果是 切片的切片,你需要手动初始化每一层。

下面的例子中,我们创建了一个 10×10 的二维整型切片

go 复制代码
var mds [][]int              // 声明一个二维切片

mds = make([][]int, 10)      // 初始化最外层切片
fmt.Println("mds =", mds)    // mds = [[] [] [] [] [] [] [] [] [] []]

for i := range mds {         // 遍历外层切片
    mds[i] = make([]int, 10) // 初始化每一个内层切片
}

代码解释

  • make([][]int, 10) 只创建了 10 个 nil[]int

  • 必须在循环中为每个元素单独 make([]int, 10)

如果是三维切片,就需要嵌套两层循环。

目前只需要记住这个概念即可。


切片赋值(Slice assignment)

切片在赋值时的行为非常像指针

来看下面的例子:

go 复制代码
var s = []int{12, 23, 34}
var sn = s

sn[0] = 0
sn[1] = 11

代码解释

这里的 sn 并不是 s 的独立副本

当你修改 sn 的元素时,s 也会被修改:

go 复制代码
fmt.Println(s)  // [0 11 34]
fmt.Println(sn) // [0 11 34]

这是因为:

  • 切片只是指向同一个底层数组

  • var sn = s 只是复制了引用,而不是数据


数组的行为不同

如果 s 是数组,那么赋值时会复制整个数据

go 复制代码
var s = [3]int{12, 23, 34} // 指定长度,变成数组
var sn = s

sn[0] = 0
sn[1] = 11

fmt.Println(s)  // [12 23 34]
fmt.Println(sn) // [0 11 34]

代码解释

  • 数组是值类型

  • 赋值会拷贝全部元素

  • 修改 sn 不会影响 s


相关推荐
自进化Agent智能体17 分钟前
Hermes Cron 定时任务 —— 让 Agent 自动工作
后端
zlinear数据采集卡25 分钟前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
我不是码神6626 分钟前
Windows 下 Codex CLI 报“拒绝访问 (os error 5)”:先查入口,再查配置
后端·chatgpt
她的男孩26 分钟前
我用 LLM 把后台 CRUD 效率提升 10 倍:AI 代码生成器的架构与落地实践
java·后端·架构
全栈技术负责人28 分钟前
解密 MCP(Model Context Protocol):大模型时代的“Type-C”总线与三驾马车架构深度解析
开发语言·ai·架构
丰锋ff35 分钟前
1.7在Qt窗口中添加右键菜单
开发语言·qt
程序员cxuan36 分钟前
本地跑一个 Qwen 3.8,你将拥有一个 Opus 4.6
人工智能·后端·程序员
Titan20241 小时前
Linux网络学习:套接字、UDP的封装与应用
linux·服务器·开发语言·网络·c++
凤山老林1 小时前
高保真集成测试:Spring Boot 结合 Testcontainers 的工程落地
spring boot·后端·集成测试
步行cgn1 小时前
MyBatis <trim> 标签完全解析:动态 SQL 的终极武器
后端